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 .

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


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


2
Весь этот вопрос касается возможности сделать это нестатическим способом, что необходимо, если вам нужны некоторые переменные экземпляра в классе.
— Саймон Форсберг

@SimonForsberg Да, и я говорю, что это проблема XY. Оператор сказал, что проблема заключается в переопределении поведения в дочерних классах. Если в примере потребовались переменные экземпляра, я мог бы предложить что-нибудь еще.
— fgb 02


@SimonForsberg Это тот комментарий, о котором я говорил. Что насчет этого?
— fgb 02
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.