【问题标题】:How to run test methods in specific order in JUnit4?如何在 JUnit4 中按特定顺序运行测试方法?
【发布时间】:2011-04-11 06:05:00
【问题描述】:

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

例如:

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

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

我认为这对 JUnit 来说是相当重要的特性,如果 JUnit 的作者不想要 order 特性,为什么?

【问题讨论】:

  • 你永远不应该编写需要以指定顺序执行的测试。这真是不好的做法。每个测试都应该能够独立运行。
  • @EJP 这对于 java pre 7 几乎是普遍适用的。在 7 之前,大多数 JVM 都这样做了,但从未得到保证。 Java 7 JVM 可以以不确定的顺序返回方法。
  • 解决。从您的测试用例中删除@Test,将它们转换为私有函数,然后创建一个测试用例,并按顺序调用私有函数。
  • 从测试用例中删除 @Test 会弄乱 JUnit 报告。顺便说一句,执行特定顺序对于单元测试来说是一种不好的做法,但对于集成测试来说不一定是一种不好的做法。最好的选择(不理想)是用@FixMethodOrder(MethodSorters.NAME_ASCENDING) 注释类,为所有测试方法保留@Test 注释,并根据所需的执行顺序按字母顺序重命名它们,例如t1_firstTest()t2_secondTest()
  • 很容易断言单元测试需要如何独立,但是以特定顺序运行测试仍然有很好的理由。就我而言,我为输入参数的 7 个可能值中的每一个运行三个单独的测试。对于每个可能的值,我想比较这三个测试,所以如果它们在输出中组合在一起,这样做会容易得多。它还帮助我识别测试失败中的模式。因此,感谢真正回答问题的人。

标签: java unit-testing junit junit4


【解决方案1】:

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

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

How do I use a test fixture?

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

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

话虽如此,如果您真的想朝这个方向发展,请考虑使用 TestNG,因为它支持以任意顺序运行测试方法(例如指定方法取决于方法组)。 Cedric Beust 在order of execution of tests in testng 中解释了如何做到这一点。

【讨论】:

  • 要么你有两个独立的测试,要么你只有一个测试并且应该这样编码。
  • @JonFreedman,据我所知,这不是测试相互依赖的情况,只是有一个要测试的规范并希望结果按该顺序显示。
  • 我可以理解不强制执行单元测试的顺序,但是当使用 JUnit 编写集成测试时,能够指定测试运行的顺序会很好。例如。先运行登录测试。
  • @BrianD。 login 可能是一个“夹具”,而不是必须在所有其他人之前运行的测试。我可能会编写一个 BeforeClass 登录,然后编写测试以按任意顺序执行。
  • 暗示“测试应该是独立的 => 测试应该是 ORDER 独立的”是不正确的。考虑对学生的作业进行自动评分。我想先针对较小的输入测试他们的解决方案,然后再针对较大的输入测试他们的解决方案。当解决方案对于较小的输入(时间/内存限制)失败时,为什么要针对较大的输入运行测试?
【解决方案2】:

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

【讨论】:

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

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

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

【讨论】:

  • 在第一个代码块中,test2 运行test1 再次。 Junit 可能仍会在 test1 之前运行 test2。这可能不是您想要的,也不是问题的有效答案。
【解决方案4】:

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

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

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

【讨论】:

  • 这种方法很好,但是如果你有超过 10 个测试它就不能正常工作,除非你添加一个 0 前缀,例如void stage01_prepareAndTest(){ }
【解决方案5】:

JUnit 自 5.5 起允许 @TestMethodOrder(OrderAnnotation.class) 用于类,@Order(1) 用于测试方法。

JUnit 旧版本允许使用类注解对测试方法运行排序:

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

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

a_TestWorkUnit_WithCertainState_ShouldDoSomething b_TestWorkUnit_WithCertainState_ShouldDoSomething c_TestWorkUnit_WithCertainState_ShouldDoSomething

或者

_1_TestWorkUnit_WithCertainState_ShouldDoSomething _2_TestWorkUnit_WithCertainState_ShouldDoSomething _3_TestWorkUnit_WithCertainState_ShouldDoSomething

你可以找到examples here

【讨论】:

    【解决方案6】:

    这是我在使用 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()”的方法开始。 谢谢!

    【讨论】:

      【解决方案7】:

      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

      结帐订单

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

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

      【讨论】:

      • @Order 无法按预期工作。它以随机方式或字母数字顺序执行方法。
      【解决方案8】:

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

      【讨论】:

      • 好像#386已经在4.11合并了。
      • @SortMethodsWith 已重命名为 @FixMethodOrderwas released in 4.11
      【解决方案9】:

      查看 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 框架的严重退化,这让许多测试开发人员的生活变得困难。

      【讨论】:

        【解决方案10】:

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

        【讨论】:

          【解决方案11】:

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

          很遗憾现在没有时间给出完整的解决方案,但是看看类:

          org.junit.runners.Suite
          

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

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

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

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

          【讨论】:

          • 自 2014 年以来您没有时间完成此答案吗? ;)
          【解决方案12】:

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

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

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

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

          【讨论】:

          • @NicolasBarbulesco 我有两个博客(俄语和英语)。这太复杂了,因为您不应该创建具有执行顺序依赖性的测试。我的解决方案是变通方法,但真正的解决方案是消除这种依赖关系。
          • 这篇文章不包含实际答案。请考虑在链接之外添加至少基本解释。
          【解决方案13】:

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

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

          【讨论】:

            【解决方案14】:

            您可以使用以下代码之一: @FixMethodOrder(MethodSorters.JVM)@FixMethodOrder(MethodSorters.DEFAULT)@FixMethodOrder(MethodSorters.NAME_ASCENDING)

            在你的测试课之前是这样的:

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

            【讨论】:

              【解决方案15】:

              正如其他人所说,理想情况下,测试应该独立于执行顺序。这使得测试不那么脆弱,并允许它们独立运行(许多 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");
                  }
              }
              

              【讨论】:

                【解决方案16】:

                是时候迁移到 Junit5了。 以下是我们可以获得的示例:

                @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
                 class OrderedTests {
                
                     @Test
                     @Order(1)
                     void nullValues() {}
                
                     @Test
                     @Order(2)
                     void emptyValues() {}
                
                     @Test
                     @Order(3)
                     void validValues() {}
                 }
                

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

                【讨论】:

                  【解决方案17】:

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

                  @FixMethodOrder(MethodSorters.JVM)
                  

                  【讨论】:

                  • @FixMethodOrder(MethodSorters.JVM):按照 JVM 返回的顺序保留测试方法。此顺序可能因运行而异。
                  • @krizajb 是的。就我而言,大多数时候,排序与源代码中的相同。这对我来说效果很好。只有当某些测试失败时,失败的测试才会在下一次运行中优先。
                  【解决方案18】:

                  JUnit 4 更新

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

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

                  灵感来源:

                  【讨论】:

                    【解决方案19】:

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

                    因此,只需按您想要的字母顺序命名您的测试。还要注意测试名称必须以 用词测试。请注意数字

                    test12 将在 test2 之前运行

                    所以:

                    testA_MyFirstTest testC_ThirdTest testB_ATestThatRunsSecond

                    【讨论】:

                      【解决方案20】:

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

                      【讨论】:

                        【解决方案21】:

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

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

                        【讨论】:

                          【解决方案22】:

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

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

                          【讨论】:

                            【解决方案23】:

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

                            @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
                            public class MyClassTest { 
                            
                                @Test
                                @Order(1)
                                public void test1() {}
                            
                                @Test
                                @Order(2)
                                public void test2() {}
                            
                            }
                            

                            【讨论】:

                              猜你喜欢
                              • 2013-10-11
                              • 2015-12-18
                              • 2010-09-05
                              • 2014-07-13
                              • 2018-08-21
                              • 2021-09-01
                              • 2023-01-08
                              • 2014-08-06
                              相关资源
                              最近更新 更多