Який порядок дзвонить Junit @ Before / @?


133

У мене є пакет тестів на інтеграцію. У мене є IntegrationTestBaseклас, щоб усі мої тести продовжили. Цей базовий клас має метод @Before( public void setUp()) та @After( public void tearDown()) для встановлення з'єднань API та DB. Те, що я роблю, - це просто переосмислення цих двох методів у кожній тестовій скриньці та виклику 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?* }
}

1
Чи є MyTestзниклий extends?
aioobe

@aioobe: вже не :)
Joel

Відповіді:


135

Так, така поведінка гарантована:

@Before:

У @Beforeметодах суперкласу будуть виконуватися до тих поточного класу, якщо вони не будуть перевизначені в поточному класі. Інше впорядкування не визначено.

@After:

Ці @Afterметоди , оголошені в суперкласі працюватимуть після того, як ті з поточного класу, якщо вони не будуть перевизначені в поточному класі.


15
Щоб було зрозуміло, порядок виконання всіх @Beforeметодів не гарантується. Якщо існує 10 @Beforeметодів, кожен з них може бути виконаний у будь-якому порядку; безпосередньо перед будь-яким іншим методом.
Шваті

5
Тож замість цитування дещо неоднозначної документації ви можете пояснити це своїми словами? Чи застосовуються @Beforeі @Afterметоди перед будь-яким іншим методом класу (один раз на метод), або безпосередньо перед і після всього набору методів класів (один раз на клас)?
BT

5
Дивіться важливий улов, заявлений Джоном Громадянином: "Це застосовується лише в тому випадку, якщо кожен метод, позначений @Before, має унікальну назву в ієрархії класів" Дуже важливо пам'ятати!
Бруно Боссола

У мене виникло конфлікт імен, використовуючи те саме ім'я методу на методі @Before (d) у класі та інший метод у його суперкласі junit-4.12.
Стефан

Чи застосовується це правило також до методів @BeforeExample ConcordionRunner?
Адріан Пронк

51

Один потенційний геть, який мене покусав раніше:

Мені подобається мати щонайменше один @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 в базовий клас? Це позбавить вас від необхідності викликати супер у всіх дітей ... все одно хороший вилов з тими ж іменами
Лоуренс Тірні

24
Лише зауваження, щоб зробити речі більш безпечними: щоб уникнути зіткнень з іменами, ви можете зробити @Before/ @Aftermethod (и) в базовому класі final, тому компілятор поскаржиться, якщо ви (випадково) спробуєте їх замінити в підкласі.
Стефан Вінклер

4
Батьківський метод з тим самим іменем, який не запускається, не здається поведінкою JUnit. Це звучить як те, як базовий режим їзди працює в OOP. Батьківський метод в основному не існує під час виконання. Дитина замінює його за всіма намірами та цілями. Ось так працює Java.
Брендон

1
Інша проблема полягає в тому, що батьківські класи повинні бути загальнодоступними, інакше їх @Beforeпозначені методи будуть ігноровані, якщо підклас також має @Beforeметод.
rusins

21

Я думаю, що виходячи з документації @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. Якщо вони хочуть, вони повинні знати точну назву і можуть це зробити.

(Незначний) недолік цього підходу: Користувачі не можуть бачити, що відбуваються речі до або після їх встановлення / tearDown. Вони повинні знати, що ці речі надаються абстрактним класом. Але я припускаю, що саме тому вони використовують абстрактний клас


2
хороший приклад - був би ще більш показовим, якби у вас було два методи @Test, тому видно, що setUp і tearDown обгортають кожен метод тестування.
Марк

Я думаю, що це основа для найкращої відповіді на ОП, але ви повинні заповнити свою відповідь окремо. Не могли б ви доповнити свій приклад, щоб охопити альтернативи, запропоновані іншими, і пояснити, чому ваша пропозиція є вищою?
wachr

2

Якщо ви обернетесь, ви можете оголосити базовий клас абстрактним, а нащадки оголосити методи setUp та tearDown (без анотацій), які в базовому класі називаються методами анотованих setUp та tearDown.


1
не погана ідея, але я не хочу виконувати контракт на тести, які не потребують власного наборуUp / tearDown
Joel

2

Ви можете використовувати @BeforeClassанотацію, щоб переконатися, що setup()вона завжди називається першою. Так само ви можете використовувати @AfterClassанотацію, щоб переконатися, що tearDown()її завжди називають останньою.

Зазвичай це не рекомендується, але це підтримується .

Це не зовсім те, що ви хочете - але, по суті, він буде тримати ваше з'єднання з БД відкритим весь час запуску ваших тестів, а потім закривати його раз і назавжди в кінці.


2
На насправді, якщо ви повинні були зробити це, я б рекомендував створити метод setupDB()і closeDB()та маркування їх @BeforeClassі @AfterClassта заміну до / після методів з setup()іtearDown()
Swati

Методи, в яких зазначається, @BeforeClassі @AfterClassмають бути статичними. Що з випадком, коли ми хочемо використовувати змінні екземпляри всередині цих методів?
Пратік Сінгал

Попередження при використанні @BeforeClassз Powermock: воно працює лише для першого тестового запуску. Дивіться цей випуск: github.com/powermock/powermock/isissue/398
Дагмар

2

Це не відповідь на питання про теги, але це відповідь на проблеми, згадані в тілі питання. Замість використання @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);
     }
 }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.