Как запустить методы тестирования в определенном порядке в JUnit4?

413

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

Например:

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

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

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

远 声 远 Shengyuan Lu
источник
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 за ответами.
Макс П Маги
Круто - доступно в JUnit 4.12
DtechNet
1
в моих тестах: testAcase - работал, test_A_case / testA_case - нет!
Родислав Молдаван
6
Я пробовал этот параметр аннотации "MethodSorters.JVM", например, "@FixMethodOrder (MethodSorters.JVM)". Из API: JVM - оставляет методы тестирования в порядке, возвращаемом JVM. Прекрасно работает для того, что я делаю (CRUD), запускает методы тестирования в том порядке, в котором они написаны. +1
Edvinauskas
1
Эта аннотация действительно является ответом, но она содержит оговорку, что она не определена (в Junit 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()простая функция, которая перебирает все возможные перестановки в коллекцию массивов.

Xiè Jìléi
источник
Но что, если тесты в разных файлах?
Олег Абражаев
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(){}
}

Это гарантирует, что методы stage2 вызываются после stage1 и перед stage3.

Joro
источник
5
Этот подход хорош, но было бы правильно упомянуть, что если у вас есть более 10 тестов, он не будет работать нормально, если вы не добавите 0префикс, напримерvoid stage01_prepareAndTest(){ }
EliuX
Если у вас более 10 этапов (не тесты) - да. Я предпочитаю ограничить количество этапов и проводить больше тестов на каждом этапе, когда это возможно.
Joro
18

Это одна из главных проблем, с которой я столкнулся, когда работал над Junit, и я нашел следующее решение, которое отлично работает для меня:

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();
}

Теперь предположим, что у вас есть класс A, в котором вы написали несколько тестов, как показано ниже:

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

void method(){

//do something

}

}

Таким образом, выполнение начнется с метода с именем "method ()". Спасибо!

Аман Гоэль
источник
Использование другого Runner JUnit с PowerMock Начиная с версии 1.6.0 PowerMock поддерживает делегирование выполнения теста другому Runner JUnit без использования правила JUnit. Это оставляет фактическое выполнение теста другому бегуну по вашему выбору. @RunWith(PowerMockRunner.class) @PowerMockRunnerDelegate(OrderedRunner.class)
Кириакос Георгиопулос
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 представляет собой @SortMethodsWith. https://github.com/junit-team/junit/pull/293 по крайней мере сделал заказ предсказуемым без этого (в Java 7 он может быть довольно случайным).

Джесси Глик
источник
1
Кажется, что # 386 был объединен в 4.11.
Джесси Глик
как отмечено ниже под этой страницей, на самом деле он @FixMethodOrder@SortMethodsWith
вызывается
6

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

То, что требует мое руководство, так это концепция индивидуального TestStep элементов , каждый из которых сообщает свои критерии прохождения / неудачи. Сбой любого шага теста не должен препятствовать выполнению последующих шагов теста.

В прошлом разработчики тестов, занимающие мою должность, организовывали классы 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

checkoutOrder

Кстати, указание @TestMethodOrder(OrderAnnotation.class)выглядит ненужным (по крайней мере, в версии 5.4.0, которую я тестировал).

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

davidxxx
источник
4

Не уверен, что согласен. Если я хочу проверить «Загрузка файла», а затем проверить «Данные, добавленные при загрузке файла», почему бы мне не захотеть, чтобы они были независимы друг от друга? Я думаю, что вполне разумно запускать их отдельно, а не в обоих тестовых примерах Голиафа.

Mattk
источник
3

То, что вы хотите, вполне разумно, когда тесты запускаются как набор.

К сожалению, сейчас нет времени дать полное решение, но взгляните на класс:

org.junit.runners.Suite

Что позволяет вызывать тестовые наборы (из любого тестового класса) в определенном порядке.

Они могут быть использованы для создания функциональных, интеграционных или системных тестов.

В результате ваши модульные тесты остаются без определенного порядка (как рекомендуется), независимо от того, выполняете ли вы их так или нет, а затем повторно используете тесты как часть более широкой картины.

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

Charlies
источник
2
У вас не было времени, чтобы завершить этот ответ с 2014 года? ;)
Чарли
2

Смотрите мое решение здесь: "Junit и Java 7."

В этой статье я опишу, как запускать тесты junit по порядку - «как в исходном коде». Тесты будут запущены, чтобы ваши методы тестирования появились в файле класса.

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

Но, как сказал Паскаль Тивент, это не очень хорошая практика.

kornero
источник
Я видел ваш пост в блоге, но это слишком сложно.
Николас Барбулеско
1
@NicolasBarbulesco У меня есть два блога (рус и англ). Это слишком сложно, потому что вы не должны создавать тесты с зависимостью порядка выполнения. Мое решение - это обходной путь, но реальное решение - это удалить эту зависимость.
Корнеро
1
Этот пост не содержит фактического ответа. Пожалуйста, рассмотрите возможность добавления хотя бы основного объяснения, помимо ссылки.
локаль по умолчанию
0

Я прочитал несколько ответов и согласен с тем, что это не лучшая практика, но самый простой способ заказать ваши тесты - и способ, которым JUnit запускает тесты по умолчанию, - это алфавитное имя по возрастанию.

Так что просто назовите ваши тесты в алфавитном порядке, который вы хотите. Также обратите внимание, что название теста должно начинаться со слова test. Просто следите за номерами

test12 будет запущен до test2

так:

testA_MyFirstTest testC_ThirdTest testB_ATestThatRunsSecond

pstorli
источник
0

Пожалуйста, проверьте это: https://github.com/TransparentMarket/junit . Он запускает тест в указанном порядке (определенном в скомпилированном файле класса). Также он включает в себя пакет AllTests для запуска тестов, определенных в подпакете. Используя реализацию AllTests, можно расширить решение, также фильтруя свойства (мы использовали аннотации @Fast, но они еще не были опубликованы).

Мартин Керстен
источник
0

Вот расширение к JUnit, которое может производить желаемое поведение: https://github.com/aafuks/aaf-junit

Я знаю, что это противоречит авторам философии JUnit, но при использовании JUnit в средах, где нет строгого модульного тестирования (как практикуется в Java), это может быть очень полезно.

shlomi33
источник
0

Я оказался здесь, думая, что мои тесты были выполнены не по порядку, но правда в том, что беспорядок был в моих асинхронных заданиях. При работе с параллелизмом вам необходимо также выполнять проверки параллельности между вашими тестами. В моем случае задания и тесты имеют общий семафор, поэтому следующие тесты зависают до тех пор, пока запущенное задание не снимет блокировку.

Я знаю, что это не полностью связано с этим вопросом, но, возможно, может помочь в определении правильного вопроса.

McCoy
источник
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 { ...}
Арезоо Багерзади
источник