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


84

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

як @BeforeClassна нестатичній функції?

Ось негарне рішення:

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

ну, це те, чого я не хочу робити, і я шукаю інтегрованого рішення для сполучень.


Ну, я маю досить велику ієрархію тестових файлів, і базові тестові файли, мені потрібна можливість замінити цю дію в дочірніх тестових класах.
Роман

1
у мене була та сама проблема, при якій лише перший із багатьох параметризованих тестів повинен виконувати вхід.
dokaspar

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при додаванні користувача TestExecutionListeners. Якщо ви це зробите, всі автопроводи будуть null.


+1 Цей прийом вирішив мою проблему, коли я хотів використовувати DbUnit і завантажувати набір даних лише один раз на клас
Бред,

+1 Це ідеально ... для людей, які не прив’язані до старовинної версії Весни. :(
Mike Miller

1
Чи буде це beforeTestClass()викликано до або після ініціалізації контексту?
Dims

@Dims після контексту ініціалізовано
Anand 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/

Єдине обмеження: працює лише для весняних випробувань.

(Я розробник цієї тестової бібліотеки)


0

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


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

@Roman: о, тепер я бачу. Додайте це до свого допису, цей коментар робить речі набагато зрозумілішими.
Роман

Конструктор буде викликаний стільки разів, скільки є тестів. Для кожного методу тестування буде створено новий об’єкт класу Test. Отже, використання конструктора тут не рішення
manikanta

Також ця робота не працює з ін'єкцією залежностей, яка покладається на вже побудований об'єкт.
Mike Miller

0

У статті обговорюються 2 дуже приємні рішення цієї проблеми:

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

0

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


Іншим варіантом, який варто розглянути, якщо використовується ін’єкція залежностей (наприклад, 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
Все це питання стосується можливості зробити це нестатичним способом, який потрібен, якщо вам потрібні якісь змінні екземпляра для класу.
Саймон Форсберг 02

@SimonForsberg Так, і я кажу, що питання є проблемою XY. Оператор заявив, що проблема полягає в перевизначенні поведінки в дитячих класах. Якщо в прикладі потрібні змінні екземпляра, то я можу запропонувати щось інше.
fgb 02


@SimonForsberg Це коментар, про який я говорив. Що з цим?
fgb
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.