В каком порядке называется Junit @ Before / @ After? - PullRequest
123 голосов
/ 20 мая 2011

У меня есть комплект интеграционных тестов.У меня есть класс 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 ]

124 голосов
/ 20 мая 2011

Да, это поведение гарантировано:

@Before:

Методы @Before для суперклассов будут выполняться до методов текущего класса, если они не переопределены в текущем классе. Никакой другой порядок не определен.

@After:

Методы @After, объявленные в суперклассах, будут выполняться после методов текущего класса, если они не переопределены в текущем классе.

47 голосов
/ 20 июня 2013

Одна потенциальная ошибка, которая укусила меня раньше:

Мне бы хотелось иметь не более одного @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();
    ...
  }
}
19 голосов
/ 09 июля 2013

Я думаю, что на основании документации @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.Им нужно знать, что эти вещи предоставляются абстрактным классом.Но я предполагаю, что именно поэтому они используют абстрактный класс

2 голосов
/ 02 ноября 2015

Это не ответ на вопрос слогана, но это ответ на проблемы, упомянутые в основной части вопроса.Вместо использования @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);
     }
 }
2 голосов
/ 20 мая 2011

Вы можете использовать аннотацию @BeforeClass, чтобы гарантировать, что setup() всегда вызывается первым. Аналогично, вы можете использовать аннотацию @AfterClass, чтобы гарантировать, что tearDown() всегда вызывается последним.

Обычно это не рекомендуется, но поддерживается .

Это не совсем то, что вы хотите - но по сути это будет держать ваше соединение с БД открытым все время, пока выполняются ваши тесты, а затем закрывать его раз и навсегда в конце.

2 голосов
/ 20 мая 2011

Если вы все измените, вы можете объявить свой базовый класс абстрактным, а потомки объявят методы setUp и tearDown (без аннотаций), которые вызываются в аннотированных методах setUp и tearDown базового класса.

...