Junit перед классом (нестатический)

84

Есть ли какие-либо рекомендации, чтобы Junit выполнял функцию один раз в тестовом файле, и она также не должна быть статической.

как @BeforeClassна нестатической функции?

Вот уродливое решение:

@Before void init(){
    if (init.get() == false){
        init.set(true);
        // do once block
    }
}

ну, это то, чего я не хочу делать, и я ищу интегрированное решение junit.

Роман
источник
Ну, у меня довольно большая иерархия тестовых файлов и базовых тестовых файлов, мне нужна возможность переопределить это действие в дочерних тестовых классах.
Роман
1
У меня была та же проблема, в которой только первый из многих параметризованных тестов должен был выполнить вход.
dokaspar 05
5
Обратите внимание, что «уродливое» решение, которое работает с простым JUnit, не принимает во внимание разрывающие тесты.
eskatos

Ответы:

22

Если вы не хотите настраивать статические инициализаторы для одноразовой инициализации и не особо заинтересованы в использовании JUnit, взгляните на TestNG. TestNG поддерживает нестатическую одноразовую инициализацию с различными вариантами конфигурации, все с использованием аннотаций.

В TestNG это было бы эквивалентно:

@org.testng.annotations.BeforeClass
public void setUpOnce() {
   // One time initialization.
}

Для разборки,

@org.testng.annotations.AfterClass
public void tearDownOnce() {
   // One time tear down.
}

Для эквивалента TestNG JUnit 4 @Beforeи @Afterвы можете использовать @BeforeMethodи @AfterMethodсоответственно.

Картик
источник
41

Кажется, неплохо работает и простой оператор if:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:test-context.xml"})
public class myTest {

    public static boolean dbInit = false;

    @Autowired
    DbUtils dbUtils;

    @Before
    public void setUp(){

        if(!dbInit){

            dbUtils.dropTables();
            dbUtils.createTables();
            dbInit = true;

        }
    }

 ...
Обновление
источник
1
Красиво и просто! Но не можете найти способ просто адаптировать это, чтобы создать нестатический @AfterClassэквивалент, который разрушается после выполнения всех тестов?
Стив Чемберс,
1
См. Здесь обновление этого метода, который должен работать для тестовых классов, использующих наследование.
Стив Чемберс,
36

Самый простой выход - использовать пустой конструктор. Вы все еще можете переопределить конструктор в расширенном классе.

Но со всем наследством это не оптимально. Вот почему JUnit 4 вместо этого использует аннотации.

Другой вариант - создать вспомогательный метод в классе factory / util и позволить этому методу выполнять всю работу.

Если вы используете Spring, вам следует рассмотреть возможность использования @TestExecutionListenersаннотации. Что-то вроде этого теста:

@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners({CustomTestExecutionListener.class, 
     DependencyInjectionTestExecutionListener.class})
@ContextConfiguration("test-config.xml")
public class DemoTest {

Spring AbstractTestExecutionListenerсодержит, например, этот пустой метод, который вы можете переопределить:

public void beforeTestClass(TestContext testContext) throws Exception {
    /* no-op */
}

ПРИМЕЧАНИЕ: НЕ упускайте из виду / пропускайте DependencyInjectionTestExecutionListenerпри добавлении custom TestExecutionListeners. Если сделаешь, то все автопроводы будут null.

Эспен
источник
+1 Этот метод решил мою проблему, когда я хотел использовать DbUnit и загружать набор данных только один раз для каждого класса
Брэд
+1 Идеально ... для людей, не привязанных к старинной версии Spring. :(
Майк Миллер
1
Будет ли это beforeTestClass()вызвано до или после инициализации контекста?
Димс
@Dims после инициализации контекста
Ананд Rockzz
7

Легко используйте @BeforeAllMethods/ @AfterAllMethodsаннотации для запуска метода внутри контекста экземпляра (нестатического), где будут доступны все введенные значения.

Для этого есть специальная библиотека тестирования:

https://mvnrepository.com/artifact/org.bitbucket.radistao.test/before-after-spring-test-runner/0.1.0

https://bitbucket.org/radistao/before-after-spring-test-runner/

Единственное ограничение: работает только для тестирования Spring .

(Я разработчик этой библиотеки тестирования)

Radistao
источник
0

Я никогда не пробовал, но, может быть, вы можете создать конструктор без аргументов и вызывать свою функцию оттуда?

Роман
источник
Это сработает, проблема в том, что мне нужна возможность переопределить это действие в классах, расширяющих этот базовый тестовый класс
Роман,
@Roman: о, теперь я понимаю. Добавьте это в свой пост, этот комментарий проясняет ситуацию.
Роман
Конструктор будет вызываться столько раз, сколько тестовых случаев. Для каждого метода тестирования будет создан новый объект класса Test. Итак, использование конструктора здесь не решение
manikanta
Также это не будет работать с инъекцией зависимостей, которая зависит от уже созданного объекта.
Майк Миллер,
0

В статье обсуждаются 2 очень хороших решения этой проблемы:

  1. "чистый" junit с пользовательским Runner (с использованием интерфейса, но вы можете расширить его с помощью пользовательской аннотации, например @BeforeInstance)
  2. Слушатели выполнения Spring, как упоминалось ранее Эспеном.
кедзи
источник
0

ОБНОВЛЕНИЕ: пожалуйста, посмотрите комментарий Черри, почему приведенное ниже предложение ошибочно. (Я сохраняю ответ здесь, а не удаляю, поскольку комментарий может предоставить полезную информацию другим о том, почему это не работает.)


Другой вариант, который стоит рассмотреть при использовании внедрения зависимостей (например, Spring) @PostConstruct. Это гарантирует, что внедрение зависимости завершено, чего не было бы в конструкторе:

@PostConstruct
public void init() {
    // One-time initialization...
}

Стив Чемберс
источник
7
Очень плохое решение в случае тестов Junit. Junit создает экземпляр тестового класса каждый раз, когда запускает тестовый метод. Итак, если в классе 6 тестовых методов, конструктор класса @Beforeи @Afterметоды будут вызываться 6 раз! Так что в этом контексте @PostConstructведет себя как @Beforeаннотация. Вы можете просто протестировать это: просто поместите 2 тестовых метода в тестовый класс, добавьте @PostConstruct public void init() {System.out.println("started");}и посмотрите в журналах, сколько раз он печатается.
Cherry
Для информации я только что наткнулся на документацию JUnit, которая подтверждает то, что описано в приведенном выше комментарии о том, что JUnit создает экземпляр для каждого @Testзапуска: «Чтобы запустить метод, JUnit сначала создает новый экземпляр класса, а затем вызывает аннотированный метод».
Стив Чемберс,
-2

Просто используйте @BeforeClass:

@BeforeClass
public static void init() {
}

Не имеет смысла initбыть нестатическим, потому что каждый тест выполняется в отдельном экземпляре. Экземпляр, на котором initзапущен, не будет соответствовать экземпляру какого-либо теста.

Единственная причина, по которой вы можете захотеть, чтобы он был нестатическим, - это переопределить его в подклассах, но вы также можете сделать это с помощью статических методов. Просто используйте то же имя, и initбудет вызван только метод подкласса .

fgb
источник
2
Весь этот вопрос касается возможности сделать это нестатическим способом, что необходимо, если вам нужны некоторые переменные экземпляра в классе.
Саймон Форсберг
@SimonForsberg Да, и я говорю, что это проблема XY. Оператор сказал, что проблема заключается в переопределении поведения в дочерних классах. Если в примере потребовались переменные экземпляра, я мог бы предложить что-нибудь еще.
fgb 02
См. Этот комментарий: stackoverflow.com/questions/2825615/…
Саймон Форсберг
@SimonForsberg Это тот комментарий, о котором я говорил. Что насчет этого?
fgb 02