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


413

我想执行带有注释的测试方法 @Test以特定顺序。

例如:

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

我想确保每次运行test1()test2()都运行MyTest,但是找不到类似的注释@Test(order=xx)

我认为对于JUnit来说这是非常重要的功能,如果JUnit的作者不希望使用订单功能,为什么呢?


2
在我看来,它们似乎按照它们在源文件中出现的顺序执行。
罗恩侯爵

84
您永远不要编写需要以指定顺序执行的测试。那真是不好的做法。每个测试都应该能够独立运行。
2012年

5
@EJP,这在Java 7 pre 7之前几乎是普遍适用的。大多数JVM都这样做了,但是从来没有保证。Java 7 JVM可以以不确定的顺序返回方法。
马修·法威尔

17
解决。从测试用例中删除@Test,将其转换为私有函数,然后创建一个测试用例,然后按顺序调用私有函数。
Simon Guo

12
从测试用例中删除@Test会弄乱JUnit报告。顺便说一句,强制执行特定顺序对于单元测试来说是一种不好的做法,但对于集成测试来说却不一定是一种不好的做法。最好的选择(不理想)是注释与类@FixMethodOrder(MethodSorters.NAME_ASCENDING),保留@Test了所有的测试方法,标注和按字母顺序命名取决于执行所需的顺序,例如t1_firstTest()t2_secondTest()等等
MisterStrickland

Answers:


238

我认为对于JUnit而言,这是非常重要的功能,如果JUnit的作者不希望使用订购功能,为什么?

我不确定用JUnit做到这一点的干净方法,据我所知JUnit假定所有测试都可以按任意顺序执行。从常见问题解答:

如何使用测试治具?

(...)测试方法调用的顺序不能得到保证,因此testOneItemCollection()可能在testEmptyCollection()之前执行。(...)

为什么会这样呢?好吧,我相信使测试依赖顺序是作者不希望提倡的做法。测试应该是独立的,不应该耦合并且违背这种意愿让事情变得难以维持,将打破单独(明显),运行等测试的能力

话虽这么说,如果您真的想朝这个方向发展,请考虑使用TestNG,因为它支持以本机的任意顺序运行测试方法(例如指定方法取决于方法组)。Cedric Beust解释了如何按照testng中的测试执行顺序执行操作


14
您有两个独立的测试,或者您只有一个测试,应该这样编写。
乔恩·弗里德曼

2
@JonFreedman,据我所知,并不是测试是相互依赖的,而是要确定要测试的事物的规格并希望结果按此顺序出现。
乔恩·布赖特

174
我可以理解不强制执行单元测试的顺序,但是当使用JUnit编写集成测试时,能够指定运行测试的顺序会很好。例如,首先运行登录测试。
Brian DiCasa 2012年

13
@BrianD。登录可能是“夹具”,而不是必须在所有其他测试之前运行的测试。我可能会编写一个登录的BeforeClass,然后编写测试以按任何顺序执行。
marcospereira 2012年

47
含义“测试应该独立=>测试应该是ORDER独立”不是正确的。考虑对学生的家庭作业进行自动评分。我想先测试它们的解决方案,然后再输入较小的输入。如果解决方案对于较小的输入失败(对于时间/内存限制),那么为什么要针对较大的输入运行测试?
mirelon

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(),但尽管它主要用于保持顺序,但不能保证-每次测试运行都有多个实例,其中007、005和006在007之后运行。您说“ WTF!”,然后运行StackOverflow以获得答案。
Max P Magee 2014年


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()是一个简单的函数,它将所有可能的置换迭代到数组的集合中。


但是,如果在不同文件中进行测试怎么办?
Oleg Abrazhaev

3
在第一个代码块中,再次test2运行。Junit的可能仍然运行之前。这可能不是您想要的,也不是问题的有效答案。test1 test2test1
toolforger

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

这样可以确保在stage1方法之后和stage3方法之前调用stage2方法。


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()”的方法开始。谢谢!


与PowerMock一起使用另一个JUnit运行器从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

您可以在此处找到示例



6

查看一个JUnit报告。JUnit已经按包进行了组织。每个软件包都具有(或可以具有)TestSuite类,每个类依次运行多个TestCases。每个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框架就足够了。因此,存在该功能是个好消息。


4

不确定是否同意,如果我要测试“文件上传”,然后测试“文件上传插入的数据”,为什么我不希望它们彼此独立?我认为完全合理,我希望能够单独运行它们,而不是在Goliath测试用例中同时运行它们。


3

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

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

org.junit.runners.Suite

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

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

无论您是否以这种方式运行单元测试,它们都没有特定的顺序(如建议的那样)(建议这样做),然后将测试用作更大的一部分。

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


2
自2014年以来,您是否没有时间完成此答案?;)
查理

2

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

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

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

但是正如Pascal Thivent所说,这不是一个好习惯。


我看过您的博客文章(俄语!),但这太复杂了。
Nicolas Barbulesco

1
@NicolasBarbulesco我有两个博客(rus和eng)。这太复杂了,因为您不创建具有执行顺序依赖性的测试。我的解决方案是解决方法,但真正的解决方案是删除该依赖性。
kornero

1
这篇文章没有实际答案。请考虑在链接之外至少添加基本说明。
默认语言区域


0

我已经阅读了一些答案,并同意它不是最佳实践,但是它是订购测试的最简单方法-默认情况下,JUnit运行测试的方法是按字母顺序升序。

因此,只需按所需的字母顺序命名测试即可。另请注意,测试名称必须以单词test开头。只是当心数字

test12将在test2之前运行

所以:

testA_MyFirstTest testC_ThirdTest testB_ATestThatRunsSecond


0

请查看以下内容:https : //github.com/TransparentMarket/junit。它按照指定的顺序(在编译的类文件中定义)运行测试。它还具有AllTests套件,可首先运行由子程序包定义的测试。使用AllTests实现,可以在筛选属性方面扩展解决方案(我们曾经使用@Fast批注,但尚未发布)。


0

这是可以产生所需行为的JUnit扩展:https : //github.com/aafuks/aaf-junit

我知道这与JUnit哲学的作者背道而驰,但是当在不是严格的单元测试(如Java中实践)的环境中使用JUnit时,这将非常有帮助。


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 { ...}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.