В каком порядке называется 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 базового класса.

Другие вопросы по тегам