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

时间:2010-09-12 03:35:27

标签: java unit-testing junit junit4

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

例如:

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

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

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

21 个答案:

答案 0 :(得分:221)

  

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

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

  

How do I use a test fixture?

     

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

为什么会这样?好吧,我相信让测试依赖于订单是作者不想推广的做法。测试应该是独立的,它们不应该被耦合并且违反这个使事情更难维护,将打破单独(显然)运行测试的能力等。

话虽如此,如果您真的想要朝着这个方向前进,请考虑使用TestNG,因为它支持以任意顺序本地运行测试方法(而且指定方法取决于方法组)。 Cedric Beust在order of execution of tests in testng中解释了如何执行此操作。

答案 1 :(得分:72)

如果您删除现有的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");
    }
}

答案 2 :(得分:48)

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

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

答案 3 :(得分:41)

迁移到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方法在stage1之后和stage3之前调用。

答案 4 :(得分:17)

这是我在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

}

}

因此,执行将从名为&#34; method()&#34;的方法开始。 谢谢!

答案 5 :(得分:8)

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

答案 6 :(得分: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()

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

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

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

答案 7 :(得分:3)

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

很遗憾没有时间立即提供完整的解决方案,但请看一下课程:

org.junit.runners.Suite

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

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

这使得您的单元测试没有特定的顺序(按照建议),无论您是否运行它们,然后重新使用测试作为更大图片的一部分。

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

答案 8 :(得分:3)

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

答案 9 :(得分:3)

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版本中)。

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

答案 10 :(得分:2)

请在此处查看我的解决方案: “Junit和java 7。”

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

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

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

答案 11 :(得分:1)

正如其他人所说,理想情况下,测试应该独立于执行顺序。这使得测试不那么脆弱,并允许它们独立运行(许多 IDE 允许您选择一种测试方法并独立于其他测试执行它)。

话虽如此,对于集成测试,有些人更喜欢依赖方法排序。

从 JUnit 4.13 开始,您可以定义自己的类,通过扩展 Ordering 来重新排序测试。有关详细信息,请参阅 the 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");
    }
}

答案 12 :(得分:0)

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

灵感来自:

答案 13 :(得分:0)

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

@FixMethodOrder(MethodSorters.JVM)

答案 14 :(得分:0)

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

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

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

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

}

答案 15 :(得分:0)

使用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

我在我的项目中正在使用它,效果很好!

答案 16 :(得分: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 { ...}

答案 17 :(得分:0)

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

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

答案 18 :(得分:0)

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

我知道这是针对JUnit哲学的作者,但是在不严格的单元测试(在Java中实践)的环境中使用JUnit时,这可能非常有用。

答案 19 :(得分:0)

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

答案 20 :(得分:0)

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

所以只需按照您想要的字母顺序命名测试。另请注意,测试名称必须开始 用测试这个词。请注意数字

test12将在test2之前运行

这样:

testA_MyFirstTest testC_ThirdTest testB_ATestThatRunsSecond