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

卢声远*_* Lu 398 java junit unit-testing junit4

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

例如:

public class MyTest {
    @Test public void test1(){}
    @Test public void test2(){}
}
Run Code Online (Sandbox Code Playgroud)

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

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

Pas*_*ent 231

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

我不确定JUnit是否有一种干净的方法,据我所知,JUnit假定所有测试都可以按任意顺序执行.来自FAQ:

我如何使用测试夹具?

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

为什么会这样?好吧,我相信让测试顺序依赖是作者不想推广的做法.测试应该是独立的,他们不应该被耦合,并且违反本将让事情变得难以维持,将打破单独(明显),运行等测试的能力

话虽这么说,如果你真的想要朝着这个方向前进,可以考虑使用TestNG,因为它支持以任意顺序本地运行测试方法(而且指定方法取决于方法组).Cedric Beust解释了如何在testng中执行测试.

  • 我可以理解不强制执行单元测试的顺序,但是当使用JUnit编写集成测试时,能够指定运行测试的顺序会很好.例如,首先运行登录测试. (169认同)
  • 暗示"测试应该是独立的=>测试应该是独立的ORDER"是不正确的.考虑自动评分学生的作业.我想首先测试他们对较小输入的解决方案,然后测试较大输入.当解决方案针对较小的输入(时间/内存限制)失败时,为什么测试应针对更大的输入运行? (44认同)
  • 要么你有两个独立的测试,要么你只有一个测试,并且应该这样编码. (13认同)
  • @BrianD.登录可能是一个"夹具"而不是必须在所有其他人之前运行的测试.我可能会编写一个登录的BeforeClass,然后编写测试以按任何顺序执行. (13认同)
  • 我怀疑 JUnit4 测试实际上是按规范顺序完成的。不能保证,因为这会 (1) 在测试人员中促进不良实践,并且会 (2) 在某种程度上束缚 JUnit 的手。 (2认同)
  • @JonFreedman,正如我理解的那样,这不是测试相互依赖的情况,只是要测试一些事物并希望结果以该顺序出现. (2认同)
  • 我可能会按照规则登录,以便类中的一个字段可以同时声明要登录和注销的测试。尽管这引发了第二个相关问题,即,如果另一个 Rule 在运行之前想要登录怎么办?现在你又有了一些订单依赖。 (2认同)
  • >>>>> TL;DR 和复活模式开启!-> JUnit 错了!如果我想在一个类中运行单元测试并假设第一个成功测试另一个(假设包含和编辑值),如果失败实际上是第一个,它将允许最后一个失败。 (2认同)
  • 注意,JUnit不按它们在类中定义的顺序运行它们.我已经看到它们以不同的顺序运行至少JUnit 4.10. (2认同)
  • @Pascal TestSuite 不能解决这个问题吗?我们使用它们来测试数据库,其中各个测试是独立的,将一些测试组合到一个更大的场景中也是有意义的,例如(创建表/插入数据/删除数据/删除表)。这是在 JUnit4 之前,所以如果 TestSuite 已被弃用或替换为某些新功能,我深表歉意。我的问题是您是否会推荐 TestSuites。 (2认同)
  • Junit 4.11现在有这个功能 - 请参阅ÖzhanDüz的其他答案 (2认同)

Ani*_*rni 83

如果您删除现有的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");
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我们实际上尝试过类似的方法test_001_login(),但是虽然它主要用于保持顺序,但是不能保证 - 我们在每次测试运行时有多个实例,其中004,005和006在007之后运行.你说,"WTF!",然后跑到StackOverflow寻求答案. (5认同)
  • 我已经尝试过这个注释参数"MethodSorters.JVM",例如"@FixMethodOrder(MethodSorters.JVM)".从API:JVM - 按照JVM返回的顺序保留测试方法.对我正在做的事情(CRUD)工作得很好,按照它们写入的顺序运行测试方法.+ 1 (5认同)
  • 在我的测试中: testAcase - 有效,test_A_case / testA_case - 没有! (2认同)
  • 这个注释确实是一个答案,但它有一个警告,它没有用`@Inherited`定义(在Junit 4.12中),因此在我的`AbstractTestCase`父类上变得无效。 (2认同)

Xiè*_*léi 49

如果订单很重要,您应该自己下订单.

@Test public void test1() { ... }
@Test public void test2() { test1(); ... }
Run Code Online (Sandbox Code Playgroud)

特别是,如有必要,您应列出一些或所有可能的订单排列以进行测试.

例如,

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(); }
Run Code Online (Sandbox Code Playgroud)

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

@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; 
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这里permute()是一个简单的函数,它将所有可能的permeations迭代到一个数组集合中.

  • 在第一个代码块中,“ test2”再次运行“ test1”。Junit可能仍在test1之前运行test2。这可能不是您想要的,也不是问题的有效答案。 (3认同)

jor*_*oro 46

迁移到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(){}
}
Run Code Online (Sandbox Code Playgroud)

这确保stage1方法在stage1之后和stage3之前调用.

  • 这种方法很不错,但如果你有10个以上的测试它将无法正常工作,除非你添加一个`0`前缀,例如`void stage01_prepareAndTest(){},这是有道理的. (5认同)

小智 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

还创建如下界面:

 @Retention(RetentionPolicy.RUNTIME)


@Target({ ElementType.METHOD})

public @interface Order {
public int order();
}
Run Code Online (Sandbox Code Playgroud)

现在假设您有A类,您已经编写了几个测试用例,如下所示:

(@runWith=OrderRunner.class)
Class A{
@Test
@Order(order = 1)

void method(){

//do something

}

}
Run Code Online (Sandbox Code Playgroud)

因此,执行将从名为"method()"的方法开始.谢谢!


dav*_*xxx 14

JUnit 5 更新(和我的意见)

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

默认情况下,单元测试库不会尝试按照源文件中出现的顺序执行测试。
JUnit 5 作为 JUnit 4 以这种方式工作。为什么 ?因为如果顺序很重要,则意味着它们之间有一些测试是耦合的,这对于单元测试来说是不可取的。
所以@NestedJUnit 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");
    }
}
Run Code Online (Sandbox Code Playgroud)

输出 :

创建用户和登录

在篮子里添加物品

结帐订单

顺便说一下,指定@TestMethodOrder(OrderAnnotation.class)看起来不需要(至少在我测试的 5.4.0 版本中)。

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


Jes*_*ick 8

(尚未发布)更改https://github.com/junit-team/junit/pull/386介绍了一个@SortMethodsWith.https://github.com/junit-team/junit/pull/293至少在没有它的情况下使订单可预测(在Java 7中它可以是非常随机的).

  • 似乎 #386 已在 4.11 中合并。 (2认同)

Zon*_*Zon 8

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

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@FixMethodOrder(MethodSorters.JVM)
@FixMethodOrder(MethodSorters.DEFAULT)
Run Code Online (Sandbox Code Playgroud)

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

a_TestWorkUnit_WithCertainState_ShouldDoSomething b_TestWorkUnit_WithCertainState_ShouldDoSomething c_TestWorkUnit_WithCertainState_ShouldDoSomething

你可以在这里找到例子.


小智 6

查看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()
Run Code Online (Sandbox Code Playgroud)

每种测试方法都断言并报告其自己的单独通过/未通过标准.为了订购,将其折叠为"一个大的测试方法"会丢失JUnit摘要报告中每个"步骤"的通过/失败标准粒度.......这让我的经理感到不安.他们目前要求另一种选择.

任何人都可以解释一个带有加扰测试方法排序的JUnit如何支持每个顺序测试步骤的单独的通过/失败标准,如上面举例说明并且我的管理层要求?

无论文档如何,我都认为这是JUnit框架中的一个严重的回归,它使许多测试开发人员的生活变得困难.


Jul*_*que 6

JUnit 4 更新

从 JUnit 4.13 开始@OrderWith,可以重现 JUnit 5@Order注释。@RunWith该解决方案比自定义 实现更好地与 JUnit 4 集成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() {}
}
Run Code Online (Sandbox Code Playgroud)
/**
 * 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();
}
Run Code Online (Sandbox Code Playgroud)
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));
}
Run Code Online (Sandbox Code Playgroud)

未注释的测试的默认优先级为 0。具有相同优先级的测试的顺序未确定。

要点: https: //gist.github.com/jcarsique/df98e0bad9e88e8258c4ab34dad3c863

灵感来自:


小智 5

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