В каком порядке называется Junit @Before/@After?
У меня есть набор тестов интеграции. у меня есть IntegrationTestBase
класс для всех моих тестов, чтобы расширить. Этот базовый класс имеет @Before
(public void setUp()
) а также @After
(public void tearDown()
) метод для установления соединения API и БД. Я просто переопределил эти два метода в каждом тестовом примере и вызвал их. super.setUp()
а также super.tearDown()
, Однако это может вызвать проблемы, если кто-то забывает вызвать супер или ставит их не туда, и возникает исключение, и они забывают вызвать супер в конечном итоге или что-то в этом роде.
Что я хочу сделать, это сделать setUp
а также tearDown
методы на базовом классе final
а затем просто добавить наш собственный аннотированный @Before
а также @After
методы. Выполняя некоторые начальные тесты, он всегда вызывает в следующем порядке:
Base @Before
Test @Before
Test
Test @After
Base @After
но я просто немного обеспокоен тем, что порядок не гарантирован и что это может вызвать проблемы. Я огляделся и ничего не увидел по этому вопросу. Кто-нибудь знает, смогу ли я это сделать и у меня не возникнет проблем?
Код:
public class IntegrationTestBase {
@Before
public final void setUp() { *always called 1st?* }
@After
public final void tearDown() { *always called last?* }
}
public class MyTest extends IntegrationTestBase {
@Before
public final void before() { *always called 2nd?* }
@Test
public void test() { *always called 3rd?* }
@After
public final void after() { *always called 4th?* }
}
6 ответов
Да, такое поведение гарантировано:
@Before
:
@Before
методы суперклассов будут запускаться до методов текущего класса, если они не переопределены в текущем классе. Никакой другой порядок не определен.
@After
:
@After
методы, объявленные в суперклассах, будут выполняться после методов текущего класса, если они не переопределены в текущем классе.
Одна потенциальная ошибка, которая укусила меня раньше:
Мне нравится иметь максимум один @Before
метод в каждом тестовом классе, потому что порядок запуска @Before
методы, определенные в классе, не гарантируются. Как правило, я буду называть такой метод setUpTest()
,
Но, хотя @Before
задокументировано как The @Before methods of superclasses will be run before those of the current class. No other ordering is defined.
, это применимо, только если каждый метод помечен @Before
имеет уникальное имя в иерархии классов.
Например, у меня было следующее:
public class AbstractFooTest {
@Before
public void setUpTest() {
...
}
}
public void FooTest extends AbstractFooTest {
@Before
public void setUpTest() {
...
}
}
Я ожидал AbstractFooTest.setUpTest()
бежать раньше FooTest.setUpTest()
, но только FooTest.setupTest()
был выполнен. AbstractFooTest.setUpTest()
не был вызван вообще.
Код должен быть изменен следующим образом для работы:
public void FooTest extends AbstractFooTest {
@Before
public void setUpTest() {
super.setUpTest();
...
}
}
Я думаю, что на основе документации @Before
а также @After
правильный вывод - дать методам уникальные имена. Я использую следующий шаблон в моих тестах:
public abstract class AbstractBaseTest {
@Before
public final void baseSetUp() { // or any other meaningful name
System.out.println("AbstractBaseTest.setUp");
}
@After
public final void baseTearDown() { // or any other meaningful name
System.out.println("AbstractBaseTest.tearDown");
}
}
а также
public class Test extends AbstractBaseTest {
@Before
public void setUp() {
System.out.println("Test.setUp");
}
@After
public void tearDown() {
System.out.println("Test.tearDown");
}
@Test
public void test1() throws Exception {
System.out.println("test1");
}
@Test
public void test2() throws Exception {
System.out.println("test2");
}
}
дать в результате
AbstractBaseTest.setUp
Test.setUp
test1
Test.tearDown
AbstractBaseTest.tearDown
AbstractBaseTest.setUp
Test.setUp
test2
Test.tearDown
AbstractBaseTest.tearDown
Преимущество такого подхода: пользователи класса AbstractBaseTest не могут случайно переопределить методы setUp/tearDown. Если они хотят, они должны знать точное имя и могут это сделать.
(Незначительный) недостаток этого подхода: пользователи не могут видеть, что что-то происходит до или после их setUp/tearDown. Им нужно знать, что эти вещи предоставляются абстрактным классом. Но я предполагаю, что именно поэтому они используют абстрактный класс
Это не ответ на вопрос слогана, но это ответ на проблемы, упомянутые в основной части вопроса. Вместо использования @Before или @After, посмотрите на использование @org.junit.Rule, потому что это дает вам больше гибкости. ExternalResource (с версии 4.7) - это правило, которое вас больше всего заинтересует, если вы управляете соединениями. Кроме того, если вы хотите гарантированный порядок выполнения ваших правил, используйте RuleChain (с 4.10). Я считаю, что все они были доступны, когда был задан этот вопрос. Пример кода ниже скопирован из Javadocs ExternalResource.
public static class UsesExternalResource {
Server myServer= new Server();
@Rule
public ExternalResource resource= new ExternalResource() {
@Override
protected void before() throws Throwable {
myServer.connect();
};
@Override
protected void after() {
myServer.disconnect();
};
};
@Test
public void testFoo() {
new Client().run(myServer);
}
}
Ты можешь использовать @BeforeClass
аннотация, чтобы убедиться, что setup()
всегда вызывается первым. Точно так же вы можете использовать @AfterClass
аннотация, чтобы убедиться, что tearDown()
всегда называется последним.
Обычно это не рекомендуется, но поддерживается.
Это не совсем то, что вам нужно, но, по сути, оно будет держать ваше соединение с БД открытым все время, пока выполняются ваши тесты, а затем закроет его раз и навсегда в конце.
Если вы все измените, вы можете объявить свой базовый класс абстрактным, а потомки объявят методы setUp и tearDown (без аннотаций), которые вызываются в аннотированных методах setUp и tearDown базового класса.