2010-01-25 3 views
65

У меня есть базовый базовый класс, который я использую в качестве базы для своих модульных тестов (TestNG 5.10). В этом классе я инициализирую всю среду для своих тестов, настройки сопоставлений базы данных и т. Д. Этот абстрактный класс имеет метод с аннотацией @BeforeClass, которая выполняет инициализацию.@BeforeClass и наследование - порядок выполнения

Далее, я расширяю этот класс с помощью специальных классов, в которых у меня есть методы @Test, а также методы @BeforeClass. Эти методы выполняют инициализацию среды, специфичную для класса (например, помещают некоторые записи в базу данных).

Как я могу применить конкретный порядок аннотированных методов @BeforeClass? Мне нужны те из абстрактного базового класса, которые должны выполняться до того, что распространяется на расширяемый класс.

Пример:

abstract class A { 
    @BeforeClass 
    doInitialization() {...} 
} 

class B extends A { 
    @BeforeClass 
    doSpecificInitialization() {...} 

    @Test 
    doTests() {...} 
} 

Ожидаемый заказ:

A.doInitialization 
B.doSpecificInitialization 
B.doTests 

Фактический порядок:

B.doSpecificInitialization // <- crashes, as the base init is missing 
(A.doInitialization  // <---not executed 
B.doTests)    // <-/ 

ответ

35

Не помещайте @BeforeClass на abstract класса. Вызовите его из каждого подкласса.

abstract class A { 
    void doInitialization() {} 
} 

class B extends A { 
    @BeforeClass 
    void doSpecificInitialization() { 
     super.doInitialization(); 
    } 

    @Test 
    void doTests() {} 
} 

Похоже TestNG имеет @BeforeClass(dependsOnMethods={"doInitialization"}) - дать ему попробовать.

+5

Это в основном то, что я хотел, чтобы избежать: нет необходимости явно вызывать методы супер (абстрактном) класса. Тем более, что у меня также есть классы, которые наследуются от A, но не имеют собственного метода @BeforeClass. Я должен был бы вставить один только для этой цели. –

+5

Обходной способ 'dependOnMethods' сделал трюк. Хотя я бы предпочел подход «суперкласса первым» ... –

+1

Чтобы использовать «dependOnMethod», не следует ли «делать инициализацию» с помощью «@Test»? Это проблема, так как технически это не тест сам по себе ... – N3da

76

редактировать: Ответ ниже для JUnit, но я оставлю это здесь в любом случае, потому что это может быть полезно.

В соответствии с JUnit api: «Методы @BeforeClass суперклассов будут выполняться до тех, что относятся к текущему классу».

Я тестировал это, и, похоже, он работает для меня.

Однако, как @Odys упоминает ниже, для JUnit вы должны иметь два метода, названные по-разному, хотя, как делать в противном случае приведет только метод подкласса выполняющиеся, потому что родитель будет затемнено.

+44

Несмотря на то, что исходный вопрос был для TestNG, я приехал сюда после поиска в Google для JUnit, и ваш ответ помог - спасибо! – teabot

+5

для JUnit вы ** нуждаетесь **, чтобы иметь два метода, названных по-разному, хотя, как иначе, приведет к запуску только метода подкласса, поскольку родитель будет затенен. – Odys

+1

@ Odys, большое спасибо за упоминание об этом. Я пытался выяснить, почему метод «setup» в моем подклассе работал, а тот, что был в его суперклассе, не был. Ты просто спас мне тонну обострения! –

6

Я просто попробовал ваш пример с 5.11, и я сначала вызвал @BeforeClass из базового класса.

Можете ли вы разместить свой файл testng.xml? Возможно, вы указываете как A, так и B там, в то время как требуется только B.

Не стесняйтесь следить за списком рассылки testng-users, и мы можем более подробно рассмотреть вашу проблему.

- Седрик

+1

Нет .xml для testng, определенного (явно), он запускается из Eclipse и Maven. –

+0

Как вы его запускаете из Eclipse? Щелкните правой кнопкой мыши на классе B? –

2

Как о том, что ваш метод @BeforeClass вызвать метод пустой specificBeforeClass(), которые могут или не могут быть перезаписаны сублимитами классов, как так:

public class AbstractTestClass { 
    @BeforeClass 
    public void generalBeforeClass() { 
    // do stuff 
    specificBeforeClass(); 
    } 

    protected void specificBeforeClass() {} 
} 

public class SpecificTest { 
    @Override 
    protected void specificBeforeClass() { 
    // Do specific stuff 
    } 

    // Tests 
} 
+2

BeforeClass должен быть статическим, поэтому вы не можете сделать это с помощью junit – madx

6

я добавил public к абстрактный класс и TestNG (6.0.1) выполнили doInitialization() до doTests.TestNG не выполняет doInitialization(), если удалить public из класса А.

public abstract class A { 
@BeforeClass 
doInitialization() {...} 
} 

class B extends A {  
@Test 
doTests() {...} 
} 
+1

Это верно, но не имеет значения. Это не работает, если класс 'B' * также * имеет метод @ @ BeforeClass'-annotated, как в случае OP. – jpaugh

+1

Я сделал то же самое. Кажется, порядок наследования пропущен, если базовый метод является закрытым. Благодаря! – Manu

2

Когда я бегу от: JUnitCore.runClasses (TestClass.class); Он будет выполнять родительский должным образом, перед ребенком (Вам не нужно super.SetUpBeforeClass();) Если вы запустите его из Eclipse: По какой-то причине не удается запустить базовый класс. Работа вокруг: Явным образом вызываю базовый класс: (BaseTest.setUpBeforeClass();)) Возможно, вы захотите иметь флаг в базовом классе, если вы запустили его из приложения, чтобы определить, если он уже настроен или нет. Таким образом, он запускается только один раз, если вы запускаете его с помощью обоих возможных методов (например, от eclipse для личного тестирования и через ANT для выпуска сборки).

Это, кажется, ошибка с Eclipse, или, по крайней мере, неожиданные результаты ..

-1

В моем случае (JUnit) У меня есть одни и те же методы, называемые установки() в базовом классе и производный класс. В этом случае только вызывается метод производного класса, и я вызываю метод базового класса.

0

Почему вы не пытаетесь создать абстрактный абстрактный метод doSpecialInit() в своем суперклассе, вызванный из аннотированного метода BeforeClass в суперклассе.

Так что разработчики, наследующие ваш класс, вынуждены реализовать этот метод.

+0

Честно говоря, даже логика, возможно, изменилась за последние 3 1/2 года с тех пор, как я задал этот вопрос ... ;-) Так что да, может быть, это была идея, может быть, это не сработало - честно говоря, Не помню. –

3

Я только что прошел через это и нашел еще один способ добиться этого. Просто используйте alwaysRun на @BeforeClass или @BeforeMethod в абстрактном классе, работает так, как вы ожидали.

public class AbstractTestClass { 
    @BeforeClass(alwaysRun = true) 
    public void generalBeforeClass() { 
     // do stuff 
     specificBeforeClass(); 
    } 
} 
1

dependsOnMethod может использоваться.

например. в случае Spring (AbstractTestNGSpringContextTests)

@BeforeClass(alwaysRun = true, dependsOnMethods = "springTestContextPrepareTestInstance") 
-2

Лучше и чище способ достичь этого, используя наследование может быть следующим -

abstract class A { 

    @BeforeClass 
    void doInitialization() {} 
} 

class B extends A { 

    @Override 
    @BeforeClass 
    void doInitialization() { 
     super.doInitialization(); 
    } 

    @Test 
    void doTests() {} 
} 
+0

Если вам нужно, чтобы метод в классе родителя выполнялся сначала, вам просто нужно назвать метод в классе Child иначе, чем родительский (потому что, если у них есть одна и та же подпись, тогда полиморфизм приходит в действие). Я считаю, что это более чистый путь. –

2

Для JUnit: Как @fortega отметил: По в JUnit api: «Методы @BeforeClass суперклассов будут выполняться до тех, что относятся к текущему классу».

Но будьте осторожны , чтобы не называть оба метода с тем же именем. Поскольку в этом случае родительский метод будет скрыт дочерним родителем. Source.

0

Здесь есть еще одно простое решение.

Моя особая ситуация заключается в том, что мне нужно вводить макеты с «BeforeClass» в подкласс до того, как выполняется «BeforeClass» в суперклассе.

Для этого просто используйте @ClassRule в подклассе.

Например:

@ClassRule 
public static ExternalResource mocksInjector = new ExternalResource() { 
    @Override 
    protected void before() { 
     // inject my mock services here 
     // Note: this is executed before the parent class @BeforeClass 
    } 
}; 

Я надеюсь, что это помогает. Это может эффективно выполнить статическую настройку в обратном порядке.

1

Проверьте свой оператор импорта. Это должно быть

import org.testng.annotations.BeforeClass;

не

import org.junit.BeforeClass;