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

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

例如:

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

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

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

461211 次浏览

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

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

(尚未发布的)更改https://github.com/junit-team/junit/pull/386引入了@SortMethodsWithhttps://github.com/junit-team/junit/pull/293至少使顺序可以预测(在Java 7中,它可以是相当随机的)。

看到我的解决方案在这里: “Junit和java 7。”< / p > 在本文中,我将描述如何按顺序运行junit测试——“就像在源代码中一样”。 测试将按照类文件中出现的测试方法的顺序运行

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

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

看一看JUnit报告。JUnit已经按包组织。每个包都有(或可以有)TestSuite类,每个类依次运行多个testcase。每个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框架中的严重倒退,使许多测试开发人员的生活变得困难。

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

所以只要按你想要的字母顺序命名你的测试。还要注意测试名称必须以开头 用单词测试。只要注意数字

Test12将在test2之前运行

所以:

< p > testA_MyFirstTest testC_ThirdTest testB_ATestThatRunsSecond < / p >

迁移到TestNG似乎是最好的方法,但是我没有看到jUnit的明确解决方案。以下是我为jUnit找到的最可读解决方案/格式化:

@FixMethodOrder( MethodSorters.NAME_ASCENDING ) // force name ordering
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方法之前被调用。

附注:我觉得这种方法比jUnit 5.5 @Order注释更好,因为它为读者提供了更好的标记。

如果你摆脱了你现有的Junit实例,并在构建路径中下载JUnit 4.11或更大的值,下面的代码将按照它们的名字的顺序执行测试方法,按升序排序:

import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;


@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");
}
}

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

不幸的是,现在没有时间给出一个完整的解决方案,但看看类:

org.junit.runners.Suite

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

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

这将使您的单元测试保持原样,没有特定的顺序(推荐),无论您是否这样运行它们,然后将这些测试作为更大图景的一部分重新使用。

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

不确定我同意,如果我想测试“文件上传”,然后测试“文件上传插入的数据”,为什么我不希望这些是相互独立的?我认为能够分开运行它们是非常合理的,而不是在一个歌利亚测试案例中同时运行它们。

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

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

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

这是我在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()”的方法开始。 谢谢!< / p >
我最终在这里认为我的测试没有按顺序运行,但事实是,混乱是在我的异步作业。在使用并发性时,您还需要在测试之间执行并发性检查。 在我的例子中,作业和测试共享一个信号量,因此下一个测试挂起,直到正在运行的作业释放锁

我知道这与这个问题并不完全相关,但也许可以帮助找到正确的问题

你可以使用这些代码中的一段: @FixMethodOrder(MethodSorters.JVM) OR @FixMethodOrder(MethodSorters.DEFAULT) OR @FixMethodOrder(MethodSorters.NAME_ASCENDING)

在你的测试类之前,像这样:

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class BookTest {...}

JUnit从5.5开始在类上允许@TestMethodOrder(OrderAnnotation.class),在测试方法上允许@Order(1)

JUnit旧版本允许测试方法使用类注释有序运行:

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@FixMethodOrder(MethodSorters.JVM)
@FixMethodOrder(MethodSorters.DEFAULT)

默认情况下,测试方法按字母顺序运行。所以,为了设置特定的方法,你可以像这样命名它们:

< p > a_TestWorkUnit_WithCertainState_ShouldDoSomething b_TestWorkUnit_WithCertainState_ShouldDoSomething c_TestWorkUnit_WithCertainState_ShouldDoSomething < / p >

< p > _1_TestWorkUnit_WithCertainState_ShouldDoSomething _2_TestWorkUnit_WithCertainState_ShouldDoSomething _3_TestWorkUnit_WithCertainState_ShouldDoSomething < / p >

你可以找到例子

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

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

你只需要注释你的类

@TestMethodOrder(OrderAnnotation.class)

https://junit.org/junit5/docs/current/user-guide/#writing-tests-test-execution-order

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

JUnit 5更新(以及我的观点)

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

默认情况下,单元测试库不会尝试按照源文件中出现的顺序执行测试。

JUnit 5作为JUnit 4的工作方式。为什么?因为如果顺序很重要,这意味着一些测试在它们之间是耦合的,这就是不适合单元测试
因此,JUnit 5引入的@Nested特性遵循相同的默认方法

但是对于集成测试,测试方法的顺序可能很重要因为一个测试方法可能会以另一个测试方法所期望的方式改变应用程序的状态。
例如,当您为电子商店结帐处理编写集成测试时,要执行的第一个测试方法是注册客户端,第二个测试方法是在购物篮中添加项目,最后一个测试方法是进行结帐。如果测试运行者不遵守这个顺序,那么测试场景就是有缺陷的,并且会失败。
因此,在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版本中是这样)。

< p > 边注
关于这个问题:JUnit 5是编写集成测试的最佳选择吗?我不认为它应该是首先考虑的工具(Cucumber和co可能经常为集成测试带来更具体的价值和特性),但在一些集成测试用例中,JUnit框架就足够了。所以这是一个好消息,该功能的存在

如果希望在JUnit 5中以特定顺序运行测试方法,可以使用下面的代码。

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class MyClassTest {


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


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


}

正如其他人所说,理想情况下,测试应该独立于执行顺序。这使得测试不那么脆弱,并允许它们独立运行(许多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");
}
}
是时候转移到Junit5了。 下面是我们可以得到的一个样本:

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


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


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


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

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

对于JUnit 4,将这个注释放在测试类上解决了这个问题。

@FixMethodOrder(MethodSorters.JVM)

JUnit 4更新

从JUnit 4.13 @OrderWith开始,可以重现JUnit 5 @Order注释。这个解决方案与JUnit 4的集成比@RunWith一个自定义的BlockJUnit4ClassRunner实现更好。

下面是如何将方法名排序(@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

启发: