Указание порядка для junit 4 тестов на уровне метода (не уровне класса)

Я знаю, что это плохая практика, но это нужно сделать, или мне нужно переключиться на testng. Есть ли способ, аналогичный JUnit 3 testSuite, указать порядок тестов, которые будут выполняться в классе?

Ответ 1

Если вы уверены, что действительно хотите это сделать: может быть, лучший способ, но это все, что я мог придумать...

JUnit4 имеет аннотацию: @RunWith, которая позволяет вам переопределить Runner по умолчанию для ваших тестов.

В вашем случае вам нужно создать специальный подкласс BlockJunit4ClassRunner и переопределить computeTestMethods(), чтобы возвращать тесты в том порядке, в котором вы их выполнили. Например, допустим, что я хочу выполнить свои тесты в обратном алфавитном порядке:

public class OrderedRunner extends BlockJUnit4ClassRunner {

    public OrderedRunner(Class klass) throws InitializationError {
        super(klass);
    }

    @Override
    protected List computeTestMethods() {
        List list = super.computeTestMethods();
        List copy = new ArrayList(list);
        Collections.sort(copy, new Comparator() {
            public int compare(FrameworkMethod o1, FrameworkMethod o2) {
                return o2.getName().compareTo(o1.getName());
            }
        });
        return copy;
    }
}
@RunWith(OrderedRunner.class)
public class OrderOfTest {
    @Test public void testA() { System.out.println("A"); }
    @Test public void testC() { System.out.println("C"); }
    @Test public void testB() { System.out.println("B"); }
}

Запуск этого теста дает:

C
B
A

В вашем конкретном случае вам нужен компаратор, который сортировал бы тесты по имени в том порядке, в котором вы их выполнили. (Я бы предложил определить компаратор, используя что-то вроде класса Google Guava Ordering.explicit("methodName1","methodName2").onResultOf(...);, где onResultOf предоставляется функция, которая преобразует FrameworkMethod в свое имя... хотя, очевидно, вы можете реализовать это так, как хотите.

Ответ 2

Я вижу несколько причин для этого, особенно при использовании JUnit для запуска функциональных тестов или тестирования постоянных объектов. Например, рассмотрим объект Article, который сохраняется в каком-то постоянном хранилище. Если я хотел бы протестировать функциональность вставки, обновления и удаления объекта Article, следуя принципу unit test, "все тесты должны быть перезаписаны и проверяться только на определенную часть функциональности", у меня было бы три теста:

  • testInsertArticle()
  • testUpdateArticle()
  • testDeleteArticle()

Однако, чтобы иметь возможность протестировать функциональность обновления, мне сначала нужно будет вставить статью. Чтобы проверить функциональность удаления, мне также нужно будет вставить статью. Таким образом, на практике функциональность вставки уже тестируется как в testUpdateArticle(), так и testDeleteArticle(). Тогда возникает соблазн просто создать тестовый метод testArticleFunctionality(), который делает все это, но такие методы в конечном итоге станут огромными (и они не будут просто проверять часть функциональности объекта Article).

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

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

Вот как он используется. Это позволяет избежать необходимости именования тестов, таких как AA_testInsert(), AB_testUpdate(), AC_testDelete(),..., ZC_testFilter() и т.д.

@RunWith(OrderedRunner.class)
public class SomethingTest {
    @Test
    @Order(order=2)
    public void testUpdateArticle() {
        // test update
    }

    @Test
    @Order(order=1)
    public void testInsertArticle() {
        // test insert
    }

    @Test
    @Order(order=3)
    public void testDeleteArticle() {
        // test delete
    }
}

Независимо от того, как эти тесты помещаются в файл, они всегда будут выполняться как order=1 сначала, order=2 второй и последний order=3, независимо от того, запускаете ли вы их из Eclipse, используя Ant, или любым другим способом.

Далее следует реализация. Во-первых, аннотация Order.

@Retention(RetentionPolicy.RUNTIME)
public @interface Order {
    public int order();
}

Затем модифицированный OrderedRunner.

public class OrderedRunner extends BlockJUnit4ClassRunner {
    public OrderedRunner(Class<?> klass) throws InitializationError {
        super(klass);
    }

    @Override
    protected List<FrameworkMethod> computeTestMethods() {
        List<FrameworkMethod> list = super.computeTestMethods();
        Collections.sort(list, 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 list;
    }
}

Ответ 3

Начиная с версии 4.11 от JUnit, можно повлиять на порядок выполнения теста, аннотируя свой класс с помощью @FixMethodOrder и указав любой из доступных MethodSorters. Подробнее см. эту ссылку.

Ответ 4

Используя junit 4.11, новая аннотация @FixMethodOrder позволяет установить определенный порядок:

@FixMethodOrder(MethodSorters.NAME_ASCENDING)

Ответ 5

Если вы хотите запускать junit-тесты в порядке "так же, как они присутствуют в вашем исходном коде", и не хотите изменять код проверки, см. мою заметку об этом здесь:

Как запускать тесты junit в порядке их присутствия в исходном коде

Но это действительно не очень хорошая идея, тесты должны быть независимыми.

Ответ 6

Код Joscarsson и Michael D в моем реестре github. Надеюсь, они не против. Я также предоставляю упорядоченную версию класса Parameterized. Он уже должен использоваться как зависимость maven

<repositories>
    <repository>
        <id>git-xxx</id>
        <url>https://github.com/crsici/OrderedRunnerJunit4.11/raw/master/</url>
    </repository>
</repositories>

<dependency>
    <groupId>com.sici.org.junit</groupId>
    <artifactId>ordered-runner</artifactId>
    <version>0.0.1-RELEASE</version>
</dependency>