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

How to run test methods in specific order in JUnit4?

我想执行由@Test按特定顺序注释的测试方法。

例如:

1
2
3
4
public class MyTest {
    @Test public void test1(){}
    @Test public void test2(){}
}

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

我认为这对JUnit来说非常重要,如果JUnit的作者不想要订单功能,为什么?


I think it's quite important feature for JUnit, if author of JUnit doesn't want the order feature, why?

我不确定是否有一个干净的方法来使用JUnit,据我所知,JUnit假定所有测试都可以以任意顺序执行。从常见问题解答:

How do I use a test fixture?

(...) The ordering of test-method invocations is not guaranteed, so testOneItemCollection() might be executed before testEmptyCollection(). (...)

为什么会这样?好吧,我认为让测试依赖于顺序是作者不希望推广的实践。测试应该是独立的,它们不应该是耦合的,违反这一点会使维护变得更困难,会破坏单独运行测试的能力(很明显),等等。

也就是说,如果您真的想要朝这个方向发展,那么考虑使用testng,因为它支持以任意顺序本地运行测试方法(比如指定方法依赖于方法组)。CedricBeust解释了如何按照在测试中执行测试的顺序来执行此操作。


如果除去现有的JUnit实例,并在构建路径中下载JUnit 4.11或更高版本,以下代码将按名称的顺序执行测试方法,并按升序排序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@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");
    }
}


如果订单很重要,你应该自己下订单。

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

特别是,如果需要,您应该列出一些或所有可能的顺序排列来测试。

例如,

1
2
3
4
5
6
7
8
9
10
11
12
13
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(); }

或者,对所有排列进行全面测试:

1
2
3
4
5
6
7
8
9
10
11
12
@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()是一个简单的函数,它将所有可能的置换迭代到数组集合中。


迁移到testng似乎是最好的方法,但是对于junit,这里没有明确的解决方案。以下是我为JUnit找到的最可读的解决方案/格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@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(){}
}

这样可以确保在Stage1方法之后和Stage3方法之前调用Stage2方法。


这是我在JUnit工作时面临的主要问题之一,我提出了以下解决方案,对我来说很好:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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;
    }
}

还可以创建如下界面:

1
2
3
4
5
6
7
8
 @Retention(RetentionPolicy.RUNTIME)


@Target({ ElementType.METHOD})

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

现在假设您有一个类,其中编写了如下几个测试用例:

1
2
3
4
5
6
7
8
9
10
11
12
(@runWith=OrderRunner.class)
Class A{
@Test
@Order(order = 1)

void method(){

//do something

}

}

所以执行将从名为"method()"的方法开始。谢谢!


(到目前为止尚未发布)更改https://github.com/junit-team/junit/pull/386引入了一个@SortMethodsWith。HTTPS://GITHUBCOM/JUITIT-TeM/JUITIT/PUL/93至少在没有这个条件的情况下可以实现顺序(在Java 7中它可以是非常随机的)。


JUnit当前允许测试方法使用类注释运行排序:

1
2
3
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@FixMethodOrder(MethodSorters.JVM)
@FixMethodOrder(MethodSorters.DEFAULT)

默认情况下,测试方法按字母顺序运行。因此,要设置特定方法的顺序,可以将其命名为:

a_TestWorkUnit_WithCertainState_ShouldDoSomething
b_TestWorkUnit_WithCertainState_ShouldDoSomething
c_TestWorkUnit_WithCertainState_ShouldDoSomething

你可以在这里找到例子。


看看JUnit报告。JUnit已经按包组织。每个包都有(或可以有)testsuite类,每个类依次运行多个测试用例。每个测试用例可以有多个形式为public void test*()的测试方法,每个方法实际上都将成为它们所属的测试用例类的实例。每个测试方法(测试用例实例)都有一个名称和一个通过/失败标准。

我的管理层需要的是单个测试步骤项目的概念,每个项目都报告自己的通过/失败标准。任何测试步骤的失败不得妨碍后续测试步骤的执行。

过去,我所在位置的测试开发人员将测试用例类组织成与被测试产品的部分相对应的包,为每个测试创建一个测试用例类,并使每个测试方法在测试中成为单独的"步骤",在JUnit输出中完成其自己的通过/失败标准。每个测试用例都是独立的"测试",但是测试用例中的各个方法或测试"步骤"必须以特定的顺序出现。

测试用例方法是测试用例的步骤,测试设计者在每个测试步骤中都获得了一个单独的通过/失败标准。现在测试步骤混乱,测试(当然)失败了。

例如:

1
2
3
4
5
6
7
8
9
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框架中的一个严重回归,这使得许多测试开发人员的生活变得困难。


不确定我同意,如果我想测试"文件上传",然后测试"通过文件上传插入的数据",为什么我不希望这些数据彼此独立?完全合理,我认为能够单独运行它们,而不是在一个歌利亚测试案例中同时运行它们。


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

不幸的是,现在没有时间给出完整的解决方案,但请看一下课程:

1
org.junit.runners.Suite

它允许您以特定的顺序调用测试用例(来自任何测试类)。

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

这将使您的单元测试没有特定的顺序(如建议的那样),不管您是否这样运行它们,然后将这些测试作为更大范围的一部分重新使用。

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


在此查看我的解决方案:"JUnit和Java 7。"

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

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

但正如帕斯卡·蒂凡特所说,这不是一个好的做法。


请查看这个:https://github.com/transparentmarket/junit。它按照指定的顺序(在编译的类文件中定义)运行测试。它还提供了一个alltests套件,可以首先运行由子包定义的测试。使用alltests实现,您还可以在筛选属性时扩展解决方案(我们曾使用@fast annotations,但尚未发布)。


我已经阅读了一些答案,并同意它不是最佳实践,而是排序测试的最简单方法——而JUnit默认运行测试的方式是按字母名称升序。

所以只要按照你想要的字母顺序来命名你的测试。还要注意测试名称必须开始通过文字测试。注意数字。

test12将在test2之前运行

所以:

测试第一次测试测试测试状态为秒


这里是JUnit的一个扩展,可以生成所需的行为:https://github.com/aafuks/aaf-junit

我知道这违背了JUnit哲学的作者,但当在不严格的单元测试环境中使用JUnit时(如在爪哇所使用的),这可能非常有用。


我最终认为我的测试没有按顺序运行,但事实是我的异步作业中存在混乱。使用并发性时,还需要在测试之间执行并发性检查。在我的例子中,作业和测试共享一个信号量,所以下一个测试挂起,直到正在运行的作业释放锁为止。

我知道这与这个问题并不完全相关,但可能有助于确定正确的问题。


您可以使用以下代码之一:

1
2
3
4
5
6
7
@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 { ...}


使用JUnit 5.4,可以指定顺序:

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

你只需要给你的班级做注解

1
@TestMethodOrder(OrderAnnotation.class)

https://junit.org/junit5/docs/current/user-guide/编写测试执行顺序

我在我的项目中使用这个,它工作得很好!