如何在JUnit4中按特定顺序运行测试方法?

472

我希望按特定顺序执行被标记为 @Test 的测试方法。

例如:

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

我希望每次运行MyTest时,都能确保在运行test2()之前先运行test1(),但我找不到类似于@Test(order=xx)的注解。

我认为这是JUnit的一个重要功能,如果JUnit的作者不想要有序功能,那么为什么呢?


103
不应编写需要按特定顺序执行的测试,这是非常不好的实践。每个测试都应该能够独立运行。 - Apfelsaft
6
在Java 7之前,几乎所有的Java虚拟机都会按照一定顺序返回方法。但是并没有得到保证。而Java 7及以后的版本的虚拟机则可以以非确定性的顺序返回方法。 - Matthew Farwell
19
绕过问题。从测试用例中删除@Test,将它们转换为私有函数,然后创建一个单一的测试用例,并按顺序调用私有函数。 - Simon Guo
17
如果从测试用例中移除@Test注解,将会影响JUnit报告。顺便提一句,强制执行特定的顺序是单元测试的坏习惯,但不一定是集成测试的坏习惯。最好的选择(虽然不完美)是使用@FixMethodOrder(MethodSorters.NAME_ASCENDING)为类进行注释,并保留所有测试方法的@Test注解,并根据期望的执行顺序按字母顺序重命名它们,例如t1_firstTest()t2_secondTest()等。 - MisterStrickland
9
谈论单元测试需要独立的问题很容易,但按特定顺序运行测试仍有非常好的理由。在我的情况下,我为每个可能的输入参数值运行三个独立的测试。对于每个可能的值,我想比较这三个测试,如果它们在输出中被分组在一起,这样做会更容易。这还有助于我识别测试失败中的模式。因此,感谢那些实际回答了问题的人。 - MiguelMunoz
显示剩余9条评论
23个回答

5

JUnit 4 更新

从 JUnit 4.13 开始,可以使用 @OrderWith 注解来模拟 JUnit 5 的 @Order 注解。这个方案比使用自定义的 BlockJUnit4ClassRunner 实现与 JUnit 4 更好地集成。

下面是如何使用注解排序替代方法名排序(@FixMethodOrder(MethodSorters.NAME_ASCENDING))的示例。

@OrderWith(OrderAnnotation.class)
public class MyTest {
    @Test
    @Order(-1)
    public void runBeforeNotAnnotatedTests() {}

    @Test
    public void notAnnotatedTestHasPriority0() {}

    @Test
    @Order(1)
    public void thisTestHasPriority1() {}

    @Test
    @Order(2)
    public void thisTestHasPriority2() {}
}

/**
 * JUnit 4 equivalent of JUnit 5's {@code org.junit.jupiter.api.Order}
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
public @interface Order {
    /**
     * Default order value for elements not explicitly annotated with {@code @Order}.
     *
     * @see Order#value
     */
    int DEFAULT = 0;

    /**
     * The order value for the annotated element.
     * <p>Elements are ordered based on priority where a lower value has greater
     * priority than a higher value. For example, {@link Integer#MAX_VALUE} has
     * the lowest priority.
     *
     * @see #DEFAULT
     */
    int value();
}

import org.junit.runner.Description;
import org.junit.runner.manipulation.Ordering;
import org.junit.runner.manipulation.Sorter;

/**
 * Order test methods by their {@link Order} annotation. The lower value has the highest priority.
 * The tests that are not annotated get the default value {@link Order#DEFAULT}.
 */
public class OrderAnnotation extends Sorter implements Ordering.Factory {
    public OrderAnnotation() {
        super(COMPARATOR);
    }

    @Override
    public Ordering create(Context context) {
        return this;
    }

    private static final Comparator<Description> COMPARATOR = Comparator.comparingInt(
            description -> Optional.ofNullable(description.getAnnotation(Order.class))
                                   .map(Order::value)
                                   .orElse(Order.DEFAULT));
}

未注释的测试默认优先级为0。具有相同优先级的测试的顺序是不确定的。
要点: https://gist.github.com/jcarsique/df98e0bad9e88e8258c4ab34dad3c863 灵感来自于:

4

当测试用例作为一个套件运行时,您的要求是完全合理的。

不幸的是,现在没有时间提供一个完整的解决方案,但是请看一下类:

org.junit.runners.Suite

这允许您按特定顺序调用任何测试类中的测试用例。

这些可能用于创建功能、集成或系统测试。

这样,您的单元测试就可以按照推荐的方式保持无特定顺序(无论您是否以此方式运行它们),然后将测试作为更大画面的一部分进行重复使用。

我们在单元测试、集成测试和系统测试中重复/继承相同的代码,有时是数据驱动的,有时是提交驱动的,有时作为套件运行。


3
自从2014年起,你没有时间完成这个回答吗? ;) - Charlie

3
正如其他人所说,测试理想情况下应该独立于执行顺序。这使得测试更加健壮,并允许它们独立运行(许多IDE允许您选择一个测试方法并独立执行它而不影响其他测试)。
话虽如此,对于集成测试,有些人更喜欢依赖于方法排序。
从JUnit 4.13开始,您可以定义自己的类来重新排序测试,方法是通过扩展Ordering。有关详细信息,请参见JUnit Wiki。以下是一个示例,使用内置的Alphanumeric类按测试方法名称按字母数字顺序排列测试:
import org.junit.Test;
import org.junit.runner.OrderWith;
import org.junit.runner.manipulation.Alphanumeric;

@OrderWith(Alphanumeric.class)
public class TestMethodOrder {

    @Test
    public void testA() {
        System.out.println("first");
    }
    @Test
    public void testB() {
        System.out.println("second");
    }
    @Test
    public void testC() {
        System.out.println("third");
    }
}

2

请查看我的解决方案: “Junit和Java 7。”

在本文中,我将描述如何按顺序运行junit测试-“就像在您的源代码中一样”。 测试将按照测试方法在类文件中出现的顺序运行。

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

但正如Pascal Thivent所说,这不是一个好的做法。


1
@NicolasBarbulesco 我有两个博客(俄语和英语)。这太复杂了,因为您不应该创建具有执行顺序依赖性的测试。我的解决方案是变通方法,但真正的解决方案是消除依赖关系。 - kornero
2
这篇文章没有实际的答案。请考虑除了链接之外,至少添加基本的解释。 - default locale

2
对于JUnit 4,将此注释放在测试类上可解决该问题。
@FixMethodOrder(MethodSorters.JVM)

@FixMethodOrder(MethodSorters.JVM):按照JVM返回的顺序保留测试方法。这个顺序可能会因为每次运行而有所不同。 - krizajb
@krizajb 说得没错。在我的情况下,大多数情况下,排序与源代码相同。对我来说这很有效。只有当一些测试失败时,下一次运行时失败的测试才会先执行。 - Wit

2

现在是时候转向Junit5了。以下是我们可能得到的示例:

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
 class OrderedTests {

     @Test
     @Order(1)
     void nullValues() {}

     @Test
     @Order(2)
     void emptyValues() {}

     @Test
     @Order(3)
     void validValues() {}
 }

对于Junit4,将您在多个测试中拥有的逻辑复制到一个测试方法中。


1
您可以使用以下代码之一: @FixMethodOrder(MethodSorters.JVM)@FixMethodOrder(MethodSorters.DEFAULT)@FixMethodOrder(MethodSorters.NAME_ASCENDING) 在您的测试类前加上这些代码。
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class BookTest {...}

1

0

这里有一个扩展程序可以实现所需的JUnit行为:https://github.com/aafuks/aaf-junit

我知道这与JUnit作者的理念相悖,但在使用JUnit进行非严格单元测试(如Java中的实践)时,这非常有帮助。


0
请查看这个链接: https://github.com/TransparentMarket/junit。它按照指定的顺序运行测试(在编译的类文件中定义)。此外,它还提供了一个AllTests套件,可以首先运行子包中定义的测试。通过使用AllTests实现,还可以扩展解决方案以过滤属性(我们曾经使用@Fast注解,但尚未发布)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接