Какой порядок называется Junit @Before/@After?
У меня есть тестовый комплект интеграции. У меня есть класс IntegrationTestBase
для всех моих тестов. Этот базовый класс имеет методы @Before
(public void setUp()
) и @After
(public void tearDown()
) для установления соединений API и DB. То, что я делаю, просто переопределяет эти два метода в каждой тестовой папке и вызывает super.setUp()
и super.tearDown()
. Однако это может вызвать проблемы, если кто-то забывает называть супер или помещает их в неправильное место, и возникает исключение, и они забывают вызвать super в конце или что-то в этом роде.
Что я хочу сделать, это сделать методы 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?* }
}
Ответы
Ответ 1
Да, это поведение гарантировано:
@Before
:
Методы @Before
суперклассов будут выполняться до тех, что относятся к текущему классу, если они не переопределены в текущем классе. Никакой другой порядок не определен.
@After
:
Методы @After
, объявленные в суперклассах, будут выполняться после тех, что относятся к текущему классу, если они не переопределены в текущем классе.
Ответ 2
Одна потенциальная добыча, которая укусила меня раньше:
Мне нравится иметь не более одного метода @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();
...
}
}
Ответ 3
Я думаю, что на основе документации @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. Им нужно знать, что эти вещи предоставляются абстрактным классом. Но я предполагаю, что причина, по которой они используют абстрактный класс
Ответ 4
Если вы измените ситуацию, вы можете объявить абстрактный абзац базового класса, а потомки объявите методы setUp и tearDown (без аннотаций), которые вызывается в методах аннотированного setUp и tearDown базового класса.
Ответ 5
Вы можете использовать аннотацию @BeforeClass
, чтобы гарантировать, что setup()
всегда вызывается первым. Аналогично, вы можете использовать аннотацию @AfterClass
, чтобы гарантировать, что tearDown()
всегда называется последним.
Обычно это не рекомендуется, но поддерживается.
Это не совсем то, что вы хотите - но это существенно упростит ваше соединение с БД все время, когда ваши тесты будут запущены, а затем закройте его раз и навсегда в конце.
Ответ 6
Это не ответ на вопрос с тегом, но это ответ на проблемы, упомянутые в теле вопроса. Вместо использования @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);
}
}