Як запустити методи тестування у визначеному порядку в JUnit4?


413

Я хочу виконати методи тестування, які зазначаються @Testв певному порядку.

Наприклад:

public class MyTest {
    @Test public void test1(){}
    @Test public void test2(){}
}

Я хочу забезпечити запуск test1()перед test2()кожним запуском MyTest, але я не зміг знайти анотацію на зразок @Test(order=xx).

Я думаю, що це досить важлива особливість для JUnit, якщо автор JUnit не хоче цього замовлення , чому?


2
Мені вони здаються виконаними в тому порядку, який вони відображаються у вихідному файлі.
Маркіз Лорн

84
Ніколи не слід писати тести, які потрібно виконати у визначеному порядку. Це дійсно погана практика. Кожен тест повинен мати можливість працювати незалежно.
Apfelsaft

5
@EJP це майже повсюдно стосується java pre 7. Попередньо 7, більшість JVM зробили це, але це ніколи не було гарантовано. Java 7 JVM можуть повертати методи в недетермінованому порядку.
Меттью Фарвелл

17
Пообіцяйте. Вилучіть @Test із тестових випадків, перетворіть їх у приватні функції, потім створіть один тестовий випадок та зателефонуйте до приватних функцій по порядку.
Саймон Го

12
Видалення @Test з тестових випадків зіпсує звіт JUnit. До речі, примусове виконання конкретного наказу є поганою практикою для тестових підрозділів, але не обов'язково є поганою практикою для інтеграційних тестів . Кращий вибір (не ідеал) анотувати клас з @FixMethodOrder(MethodSorters.NAME_ASCENDING), зберегти @Testанотацію для всіх методів тестування і перейменувати їх в алфавітному порядку в залежності від необхідного порядку виконання, наприклад t1_firstTest(), t2_secondTest()і т.д.
MisterStrickland

Відповіді:


238

Я думаю, що це досить важлива особливість для JUnit, якщо автор JUnit не хоче функцію замовлення, чому?

Я не впевнений, що існує чистий спосіб зробити це з JUnit, наскільки мені відомо, JUnit передбачає, що всі тести можуть бути виконані у довільному порядку. З FAQ:

Як використовувати тестовий кріплення?

(...) Порядок викликів методу тестування не гарантується , тому testOneItemCollection () може бути виконаний перед testEmptyCollection (). (...)

Чому так? Ну, я вважаю, що зробити замовлення тестів залежним - це практика, яку автори не хочуть просувати. Тести повинні бути незалежними, вони не повинні поєднуватися і порушувати цю волю зробити все важче підтримувати, зламається можливість запускати тести по окремо ( що очевидно), і т.д.

Якщо говорити, якщо ви дійсно хочете піти в цьому напрямку, подумайте про використання TestNG, оскільки він підтримує запуск методів тестів у будь-якому довільному порядку (і такі речі, як уточнення, що методи залежать від груп методів). Седрік Беуст пояснює, як це зробити в порядку виконання тестів у testng .


14
Або у вас є два незалежних тесту, або у вас є лише один тест, і його слід кодувати.
Джон Фрідман

2
@JonFreedman, наскільки я розумію питання, це не випадок взаємозалежності тестів, а лише те, що потрібно перевірити специфіку речей і бажати, щоб результати відображалися в такому порядку.
Джон Брайт

174
Я можу зрозуміти, що не виконувати замовлення для одиничних тестів, однак при використанні JUnit для запису інтеграційних тестів було б непогано мати змогу вказати порядок виконання тестів. Наприклад, спочатку запустіть тест на вхід.
Брайан Дікаса

13
@BrianD. Вхід, ймовірно, є "кріпленням" замість тесту, який повинен працювати перед усіма іншими. Я, мабуть, напишу BeforeClass, який входить у систему, а потім напишу тести для виконання в будь-якому порядку.
marcospereira

47
Значення "тести повинні бути незалежними => тести повинні бути ЗАМОВЛЕННЯМ" не відповідає дійсності. Розгляньте автоматичне оцінювання домашніх завдань учнів. Я хочу перевірити їх рішення спочатку для менших входів, а для великих входів пізніше. Якщо рішення не вдається для менших входів (для обмеження часу / пам'яті), то навіщо тести виконуватись на більші входи?
мирелон

96

Якщо ви позбудетесь від наявного примірника Junit і завантажите JUnit 4.11 або новішої версії на шляху збірки, наступний код виконує методи тестування у порядку їх імен, відсортованих у порядку зростання:

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class SampleTest {

    @Test
    public void testAcreate() {
        System.out.println("first");
    }
    @Test
    public void testBupdate() {
        System.out.println("second");
    }
    @Test
    public void testCdelete() {
        System.out.println("third");
    }
}

8
Наприклад, ми спробували подібний метод test_001_login (), але, хоча він здебільшого працює для збереження порядку, це не гарантується - у нас є кілька примірників за тестовий пробіг, де 004, 005 та 006 запускаються після 007. Це робить Ви кажете: "WTF !," і біжіть до StackOverflow за відповідями.
Max P Magee

Чудовий - доступний у
січні

1
у моїх тестах: testAcase - працював, test_A_case / testA_case - не робив!
Родіслав Молдован

6
Я спробував цей параметр анотації "MethodSorters.JVM", наприклад "@FixMethodOrder (MethodSorters.JVM)". З API: JVM - залишає методи тестування у порядку, поверненому JVM. Мені добре підходить те, що я роблю (CRUD), використовує методи тестування в тому порядку, про який вони написані. +1
Edvinauskas

1
Ця анотація справді є відповіддю, але вона має застереження, що вона не визначена (у червні 4.12) @Inheritedі тому стає неефективною для мого AbstractTestCaseбатьківського класу.
AbVog

49

Якщо замовлення важливе, ви повинні зробити замовлення самостійно.

@Test public void test1() { ... }
@Test public void test2() { test1(); ... }

Зокрема, вам слід перелічити деякі або всі можливі перестановки замовлення для тестування, якщо це необхідно.

Наприклад,

void test1(); 
void test2(); 
void test3(); 


@Test
public void testOrder1() { test1(); test3(); }

@Test(expected = Exception.class)
public void testOrder2() { test2(); test3(); test1(); }

@Test(expected = NullPointerException.class)
public void testOrder3() { test3(); test1(); test2(); }

Або повний тест усіх перестановок:

@Test
public void testAllOrders() {
    for (Object[] sample: permute(1, 2, 3)) {
        for (Object index: sample) {
            switch (((Integer) index).intValue()) {
                case 1: test1(); break; 
                case 2: test2(); break; 
                case 3: test3(); break; 
            }
        }
    }
}

Тут permute()проста функція, яка ітералізує всі можливі перестановки в колекцію масиву.


Але що робити, якщо тести в різних файлах?
Олег Абражаєв

3
У першому кодовому блоці test2запускається test1 знову . Хуніт все ще може працювати test2раніше test1. Ймовірно, це не те, що ви задумали, і не є коректною відповіддю на питання.
інструментарій

47

Міграція до TestNG здається найкращим способом, але я не бачу тут чіткого рішення для jUnit. Ось найбільш читабельне рішення / форматування, яке я знайшов для jUnit:

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class SampleTest {
    @Test
    void stage1_prepareAndTest(){};

    @Test
    void stage2_checkSomething(){};

    @Test
    void stage2_checkSomethingElse(){};

    @Test
    void stage3_thisDependsOnStage2(){};

    @Test
    void callTimeDoesntMatter(){}
}

Це забезпечує етап2 виклики після етапу1 та перед стадією3.


5
Цей підхід є приємним, але було б справедливо зазначити, що якщо у вас більше 10 тестів, він не буде добре працювати, якщо ви не додасте 0префікс, наприкладvoid stage01_prepareAndTest(){ }
EliuX

Якщо у вас більше 10 етапів (не тестів) - Так. Я вважаю за краще обмежувати кількість етапів і мати більше тестів на кожному етапі, коли це можливо.
Жоро

18

Це одне з головних питань, з якими я стикався, коли працював над Юнітом, і я придумав таке рішення, яке для мене добре працює:

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;

public class OrderedRunner extends BlockJUnit4ClassRunner {

    public OrderedRunner(Class<?> clazz) throws InitializationError {
        super(clazz);
    }

    @Override
    protected List<FrameworkMethod> computeTestMethods() {
        List<FrameworkMethod> list = super.computeTestMethods();
        List<FrameworkMethod> copy = new ArrayList<FrameworkMethod>(list);
        Collections.sort(copy, new Comparator<FrameworkMethod>() {

            @Override
            public int compare(FrameworkMethod f1, FrameworkMethod f2) {
                Order o1 = f1.getAnnotation(Order.class);
                Order o2 = f2.getAnnotation(Order.class);

                if (o1 == null || o2 == null) {
                    return -1;
                }

                return o1.order() - o2.order();
            }
        });
        return copy;
    }
}

також створіть інтерфейс, як показано нижче:

 @Retention(RetentionPolicy.RUNTIME)


@Target({ ElementType.METHOD})

public @interface Order {
public int order();
}

Тепер припустимо, що у вас є клас А, де ви написали кілька тестових випадків, як нижче:

(@runWith=OrderRunner.class)
Class A{
@Test
@Order(order = 1)

void method(){

//do something

}

}

Тож виконання почнеться з методу з назвою "method ()". Дякую!


Використання іншого JUnit Runner з PowerMock З версії 1.6.0 PowerMock має підтримку для делегування тестового виконання на інший бігун JUnit без використання правила JUnit. Це залишає фактичне виконання тесту іншому бігуну на ваш вибір. @RunWith(PowerMockRunner.class) @PowerMockRunnerDelegate(OrderedRunner.class)
Kyriakos Georgiopoulos

11

В даний час JUnit дозволяє методам тестування виконувати замовлення з використанням анотацій класу:

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@FixMethodOrder(MethodSorters.JVM)
@FixMethodOrder(MethodSorters.DEFAULT)

За замовчуванням методи тестування виконуються в алфавітному порядку. Отже, щоб встановити порядок конкретних методів, ви можете назвати їх такими:

a_TestWorkUnit_WithCertainState_ShouldDoSomething b_TestWorkUnit_WithCertainState_ShouldDoSomething c_TestWorkUnit_WithCertainState_ShouldDoSomething

Ви можете знайти приклади тут .


8

Зміна (поки що не вийшла ) https://github.com/junit-team/junit/pull/386 вводить a @SortMethodsWith. https://github.com/junit-team/junit/pull/293 принаймні зробив замовлення передбачуваним без цього (у Java 7 це може бути цілком випадковим).


1
Здається, що № 386 було об'єднано в 4.11.
Джессі Глік

як зазначається далі під цією сторінкою, це насправді називається @FixMethodOrderне @SortMethodsWithв 4.11
ворбургері

6

Подивіться звіт JUnit. JUnit вже організований пакетом. У кожному пакеті є (або може бути) клас TestSuite, кожен з яких у свою чергу запускає декілька TestCases. Кожна TestCase може мати кілька методів тестування формиpublic void test*() , кожен з яких фактично стане екземпляром класу TestCase, до якого вони належать. Кожен метод тестування (екземпляр TestCase) має ім'я та критерії пропуску / відмови.

Що вимагає моє управління - це концепція індивідуального тестування елементів , кожен з яких повідомляє про власні критерії проходження / відмови. Відмова будь-якого етапу тесту не повинна перешкоджати виконанню наступних етапів випробувань.

Раніше розробники тестів в моєму положенні організовували заняття TestCase в пакети, що відповідають частинам продукту, що перевіряється, створювали клас TestCase для кожного тесту і робили кожен метод тестування окремим "кроком" у тесті, комплектується власними критеріями пропуску / відмови у виході JUnit. Кожен TestCase - це окремий "тест", але окремі методи, або тестові "етапи" в межах TestCase, повинні проходити у визначеному порядку.

Методи TestCase були етапами TestCase, і конструктори тестів отримали окремий критерій проходження / відмови на крок випробування. Тепер тестові кроки змішані, а тести (звичайно) провалюються.

Наприклад:

Class testStateChanges extends TestCase

public void testCreateObjectPlacesTheObjectInStateA()
public void testTransitionToStateBAndValidateStateB()
public void testTransitionToStateCAndValidateStateC()
public void testTryToDeleteObjectinStateCAndValidateObjectStillExists()
public void testTransitionToStateAAndValidateStateA()
public void testDeleteObjectInStateAAndObjectDoesNotExist()
public void cleanupIfAnythingWentWrong()

Кожен метод тестування затверджує та повідомляє про свої окремі критерії проходження / відмови. Складаючи це на "один великий метод тестування" заради замовлення, втрачається деталізація критеріїв проходження / відмови кожного "кроку" у зведеному звіті JUnit. ... і це засмучує моїх менеджерів. Зараз вони вимагають іншої альтернативи.

Чи може хто-небудь пояснити, як JUnit з замовленим методом тестування підтримує окремі критерії проходження / відмови кожного послідовного етапу тестування, як пояснено вище та вимагається моїм керівництвом?

Незалежно від документації, я вважаю це серйозним регресом в рамках JUnit, що ускладнює життя багатьом розробникам тестів.


6

Оновлення JUnit 5 (і моя думка)

Я думаю, що це досить важлива особливість для JUnit, якщо автор JUnit не хоче функцію замовлення, чому?

За замовчуванням бібліотеки модульних тестувань не намагаються виконувати тести у тому порядку, який відбувається у вихідному файлі.
JUnit 5 як JUnit 4 працює таким чином. Чому? Тому що, якщо порядок має значення, це означає, що деякі тести з'єднані між ними, і це не бажано для одиничних тестів .
Тож @Nestedфункція, представлена ​​JUnit 5, дотримується того самого підходу за замовчуванням.

Але для інтеграційних тестів порядок методу тестування може мати значення, оскільки метод тестування може змінити стан програми так, як очікується іншим методом тестування. Наприклад, коли ви пишете тест на інтеграцію для обробки замовлення електронного магазину, перший метод тесту, який слід виконати, це реєстрація клієнта, другий - додавання елементів у кошик, а останній - оформлення замовлення. Якщо тестовий виконавець не дотримується цього порядку, сценарій тестування є помилковим і не працює.
Отже, у JUnit 5 (з версії 5.4) у вас є однакова можливість встановити порядок виконання, анотувавши тестовий клас за допомогою @TestMethodOrder(OrderAnnotation.class)та вказавши порядок із@Order(numericOrderValue) методами, які мають значення для замовлення.

Наприклад :

@TestMethodOrder(OrderAnnotation.class) 
class FooTest {

    @Order(3)
    @Test
    void checkoutOrder() {
        System.out.println("checkoutOrder");
    }

    @Order(2)
    @Test
    void addItemsInBasket() {
        System.out.println("addItemsInBasket");
    }

    @Order(1)
    @Test
    void createUserAndLogin() {
        System.out.println("createUserAndLogin");
    }
}

Вихід:

createUserAndLogin

addItemsInBasket

замовленняЗамовник

До речі, уточнюючи @TestMethodOrder(OrderAnnotation.class) виглядає не потрібною (принаймні у тестованій 5.4.0 версії).

Побічна примітка
Про питання: чи є JUnit 5 найкращим вибором для написання інтеграційних тестів? Я не думаю, що це має бути першим інструментом для розгляду (огірок та спільно часто можуть приносити більш конкретну цінність та можливості інтеграційних тестів), але в деяких тестових випадках інтеграції рамки JUnit достатньо. Тож це гарна новина, що функція існує.


4

Не впевнений, що я згоден. Якщо я хочу перевірити "Завантаження файлів", а потім перевірити "Дані, вставлені файлом завантаження", чому я не хочу, щоб вони не залежали один від одного? Цілком розумним я вважаю, що зможу запускати їх окремо, а не мати те й інше в тестовому випадку Goliath.


3

Те, що ви хочете, цілком розумно, коли тестові справи ведуться як комплект.

На жаль, зараз немає часу, щоб дати повне рішення, але подивіться на клас:

org.junit.runners.Suite

Що дозволяє викликати тестові випадки (з будь-якого класу тестів) у визначеному порядку.

Вони можуть бути використані для створення функціональних, інтеграційних або системних тестів.

Це залишає ваші тестові одиниці, оскільки вони є без конкретного порядку (як рекомендується), незалежно від того, запускаєте ви їх так чи ні, а потім повторно використовуєте тести як частину більшої картини.

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


2
Ви не мали часу виконати цю відповідь з 2014 року? ;)
Чарлі

2

Дивіться моє рішення тут: "Хуніт і ява 7."

У цій статті я описую, як запустити тести на день, щоб - «так само, як у вашому вихідному коді». Тести будуть запускатися для того, щоб ваші методи тестування відображалися у файлі класу

http://intellijava.blogspot.com/2012/05/junit-and-java-7.html

Але, як сказав Паскаль Тівеннт, це не є хорошою практикою.


Я бачив вашу публікацію в блозі (російською мовою!), Але це занадто складно.
Ніколя Барбулеско

1
@NicolasBarbulesco У мене є два блоги (рус та англ). Це занадто складно, тому що ви не створюєте тести із залежністю порядку виконання. Моє рішення є вирішувальним, але справжнє рішення - це усунути цю залежність.
kornero

1
Ця публікація не містить фактичної відповіді. Будь ласка, подумайте про додавання хоча б основного пояснення, крім посилання.
локаль за замовчуванням

1

За допомогою JUnit 5.4 ви можете вказати порядок:

@Test
@Order(2)
public void sendEmailTestGmail() throws MessagingException {

вам просто потрібно анотувати свій клас

@TestMethodOrder(OrderAnnotation.class)

https://junit.org/junit5/docs/current/user-guide/#writing-tests-test-execution-order

Я використовую це у своєму проекті, і він працює дуже добре!


0

Я прочитав декілька відповідей і погоджуюся з не найкращою практикою, але найпростіший спосіб замовити свої тести - а спосіб, коли JUnit виконує тести за замовчуванням, - це алфавітне ім'я у порядку зростання.

Тому просто назвіть свої тести в алфавітному порядку, який вам потрібно. Також зверніть увагу, що назва тесту повинна починатися зі слова тест. Просто стежте за номерами

test12 запуститься до test2

тому:

testA_MyFirstTest testC_ThirdTest тестB_ATestThatRunsSecond


0

Перевірте це: https://github.com/TransparentMarket/junit . Він запускає тест у тому порядку, в якому вони вказані (визначені в складеному файлі класу). Крім того, він має набір AllTests для запуску тестів, визначених спочатку підпакетом. Використовуючи реалізацію AllTests, можна розширити рішення і для фільтрування властивостей (ми використовували анотації @Fast, але вони ще не були опубліковані).


0

Ось розширення до JUnit, яке може спричинити бажану поведінку: https://github.com/aafuks/aaf-junit

Я знаю, що це проти авторів філософії JUnit, але при використанні JUnit в середовищах, які не є суворими тестовими одиницями (як це практикується на Java), це може бути дуже корисним.


0

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

Я знаю, що це не повністю пов'язане з цим питанням, але, можливо, це може допомогти орієнтуватися на правильну проблему


0

ви можете використовувати один із цих кодів:

@FixMethodOrder(MethodSorters.JVM)OR `@FixMethodOrder(MethodSorters.DEFAULT)` OR `@FixMethodOrder(MethodSorters.NAME_ASCENDING)` before your test class like this:


@FixMethodOrder(MethodSorters.NAME_ASCENDING)


public class BookTest { ...}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.