【问题标题】:Easy way of running the same junit test over and over?一遍又一遍地运行相同的junit测试的简单方法?
【发布时间】:2010-12-02 08:19:33
【问题描述】:

正如标题所说,我正在寻找一种简单的方法来使用 Eclipse 自动连续多次运行 JUnit 4.x 测试。

一个例子是连续运行 10 次相同的测试并报告结果。

我们已经有一种复杂的方法来做到这一点,但我正在寻找一种简单的方法来做到这一点,以便我可以确定我一直在尝试修复的不稳定测试保持不变。

理想的解决方案是我不知道的 Eclipse 插件/设置/功能。

【问题讨论】:

  • 我很好奇你为什么要这样做。
  • 我正在运行一个大的黑盒测试,做了一个小改动,想看看这对以前不稳定的测试的稳定性有何影响。
  • 确实如此,只是你想让它一直运行到失败,而我只是想运行它多次,这可能会影响我得到的答案。
  • 你反对 TestNG,因为如果不是,那么你可以使用 @Test(invocationCount = 10),这就是它的全部内容。
  • 我不是“反对”TestNG,我们只是没有在那个项目中使用它。

标签: java eclipse junit


【解决方案1】:

受以下资源启发:

示例

如下创建和使用@Repeat注解:

public class MyTestClass {

    @Rule
    public RepeatRule repeatRule = new RepeatRule();

    @Test
    @Repeat(10)
    public void testMyCode() {
        //your test code goes here
    }
}

重复.java

import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.METHOD;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention( RetentionPolicy.RUNTIME )
@Target({ METHOD, ANNOTATION_TYPE })
public @interface Repeat {
    int value() default 1;
}

RepeatRule.java

import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;

public class RepeatRule implements TestRule {

    private static class RepeatStatement extends Statement {
        private final Statement statement;
        private final int repeat;    

        public RepeatStatement(Statement statement, int repeat) {
            this.statement = statement;
            this.repeat = repeat;
        }

        @Override
        public void evaluate() throws Throwable {
            for (int i = 0; i < repeat; i++) {
                statement.evaluate();
            }
        }

    }

    @Override
    public Statement apply(Statement statement, Description description) {
        Statement result = statement;
        Repeat repeat = description.getAnnotation(Repeat.class);
        if (repeat != null) {
            int times = repeat.value();
            result = new RepeatStatement(statement, times);
        }
        return result;
    }
}

PowerMock

将此解决方案用于@RunWith(PowerMockRunner.class),需要更新到Powermock 1.6.5(包括a patch)。

【讨论】:

【解决方案2】:

这本质上是 Yishai 在上面提供的答案,用 Kotlin 重写:

@RunWith(Parameterized::class)
class MyTest {

    companion object {

        private const val numberOfTests = 200

        @JvmStatic
        @Parameterized.Parameters
        fun data(): Array<Array<Any?>> = Array(numberOfTests) { arrayOfNulls<Any?>(0) }
    }

    @Test
    fun testSomething() { }
}

【讨论】:

    【解决方案3】:

    执行此操作的最简单(因为需要最少的新代码量)方法是将测试作为参数化测试运行(使用 @RunWith(Parameterized.class) 注释并添加提供 10 个空参数的方法)。这样框架将运行 10 次测试。

    这个测试需要是类中唯一的测试,或者更好的是所有测试方法应该需要在类中运行 10 次。

    这是一个例子:

    @RunWith(Parameterized.class)
    public class RunTenTimes {
    
        @Parameterized.Parameters
        public static Object[][] data() {
            return new Object[10][0];
        }
    
        public RunTenTimes() {
        }
    
        @Test
        public void runsTenTimes() {
            System.out.println("run");
        }
    }
    

    使用上述方法,甚至可以使用无参数构造函数来实现,但我不确定框架作者是否有意这样做,或者将来是否会中断。

    如果您正在实现自己的运行器,那么您可以让运行器运行测试 10 次。如果您使用第三方运行器,那么在 4.7 中,您可以使用新的 @Rule 注释并实现 MethodRule 接口,以便它获取语句并在 for 循环中执行 10 次。这种方法目前的缺点是@Before@After 只运行一次。这可能会在 JUnit 的下一个版本中发生变化(@Before 将在@Rule 之后运行),但无论您将在对象的同一实例上进行操作(Parameterized 跑步者并非如此)。这假设无论您运行该类的哪个运行器都能正确识别 @Rule 注释。仅当它委托给 JUnit 运行器时才会出现这种情况。

    如果您使用无法识别 @Rule 注释的自定义运行程序运行,那么您真的不得不编写自己的运行程序,该运行程序适当地委托给该运行程序并运行 10 次。

    请注意,还有其他可能解决此问题的方法(例如 Theories 跑步者),但它们都需要跑步者。不幸的是,JUnit 目前不支持运行层。那是一个束缚其他跑步者的跑步者。

    【讨论】:

    • 不幸的是,我已经在与另一个跑步者一起运行@RunWith,但否则这将是一个理想的解决方案。
    • 是的,这是我想要的解决方案,对大多数人来说是最好的,所以我将继续接受答案。
    • 有关另一种可能不那么老套的解决方案,请参阅:stackoverflow.com/a/21349010/281545
    • 不错的解决方案!我收到一个异常,告诉我 data 方法应该返回一个 Iterable 的数组。我相应地对其进行了修复:@Parameterized.Parameters public static Iterable data() { return Arrays.asList(new Object[20][0]); }
    • 您能否为 JUnit 5 链接到 this answer?它描述了在 JUnit 5 中添加的请求功能
    【解决方案4】:

    使用 IntelliJ,您可以从测试配置中执行此操作。打开此窗口后,您可以选择运行测试任意次数。

    当您运行测试时,intellij 将按照您指定的次数执行您选择的所有测试。

    运行 624 次测试 10 次的示例:

    【讨论】:

    • 太完美了,现在如果你能指出一种日食的做法,那将回答 OP 的问题
    • 依赖特定工具来承载实际逻辑或需求是一种反模式。
    • @Mickael 重复测试 N 次通常不是测试的要求。事实上,测试应该是确定性的,因此无论重复多少次,它都应该始终产生相同的结果。你能解释一下你所说的反模式吗?
    • 如果重复测试对 1 个开发人员有用,那么它可能对其他人有用。因此,如果测试运行时和代码可以承载启用重复的逻辑,则应该首选它,因为它允许分解工作量和解决方案,并允许贡献者使用所需的工具并获得相同的结果。当可以将可重用逻辑放入代码中时,将其放入 IDE/开发人员区域是一种缺少分解。
    • 在最新的 IDEA 版本 (v2021.2.3) 中,这隐藏在“修改选项 (Alt+M) -> 测试 |重复 - 一次',选择 N 次使文本框为否。出现重复次数
    【解决方案5】:

    我发现 Spring 的重复注解对这种事情很有用:

    @Repeat(value = 10)
    

    最新(Spring Framework 4.3.11.RELEASE API)文档:

    【讨论】:

    • 更改测试框架并不是我所说的简单方法。
    • 你不需要改变你的测试框架——它在 JUnit 上工作得很好。主要缺点是 JUnit 仍将其视为单个测试。所以第一次中断时,执行将停止。但是,如果您还没有使用 Spring,那么它可能不是您想要的方式......
    • 似乎对我不起作用(Spring 4.3.6 通过 Spring Boot 1.5.1)
    • 不适用于我的 spring boot 2.1.6 和 junit 5
    • 与 spring boot 2 完美配合。不要忘记添加@RunWith(SpringRunner::class),按照海报的“春季单元测试”链接!
    【解决方案6】:

    您可以从 main 方法运行您的 JUnit 测试并重复多次:

    package tests;
    
    import static org.junit.Assert.*;
    
    import org.junit.Test;
    import org.junit.runner.Result;
    
    public class RepeatedTest {
    
        @Test
        public void test() {
            fail("Not yet implemented");
        }
    
        public static void main(String args[]) {
    
            boolean runForever = true;
    
            while (runForever) {
                Result result = org.junit.runner.JUnitCore.runClasses(RepeatedTest.class);
    
                if (result.getFailureCount() > 0) {
                    runForever = false;
                   //Do something with the result object
    
                }
            }
    
        }
    
    }
    

    【讨论】:

      【解决方案7】:

      使用 JUnit 5,我可以使用 @RepeatedTest 注释解决这个问题:

      @RepeatedTest(10)
      public void testMyCode() {
          //your test code goes here
      }
      

      请注意,@Test 注释不应与 @RepeatedTest 一起使用。

      【讨论】:

        【解决方案8】:

        tempus-fugit 库中有一个 Intermittent 注释,可与 JUnit 4.7 的 @Rule 一起使用以重复测试多次或与 @RunWith 一起使用。

        例如,

        @RunWith(IntermittentTestRunner.class)
        public class IntermittentTestRunnerTest {
        
           private static int testCounter = 0;
        
           @Test
           @Intermittent(repition = 99)
           public void annotatedTest() {
              testCounter++;
           }
        }
        

        运行测试后(使用 @RunWith 中的 IntermittentTestRunner),testCounter 将等于 99。

        【讨论】:

        • 是的,这里也是同样的问题,已经在使用另一个跑步者,所以不能使用这个,不过好主意。
        • 是的,我在使用 RunWith 时遇到了同样的问题...我调整了 tempus-fugit 以稍微绕过它,您可以在需要时使用 @Rule 而不是 runner反复运行。你用@Repeating 标记它而不是断断续续的。不过,规则版本不会运行 @Before/@Afters。有关详细信息,请参阅tempus-fugit.googlecode.com/svn/site/documentation/…(向下滚动到加载/浸泡测试)。
        【解决方案9】:

        我构建了一个允许进行此类测试的模块。但它的重点不仅在于重复。但是为了保证某些代码是线程安全的。

        https://github.com/anderson-marques/concurrent-testing

        Maven 依赖:

        <dependency>
            <groupId>org.lite</groupId>
            <artifactId>concurrent-testing</artifactId>
            <version>1.0.0</version>
        </dependency>
        

        使用示例:

        package org.lite.concurrent.testing;
        
        import org.junit.Assert;
        import org.junit.Rule;
        import org.junit.Test;
        import ConcurrentTest;
        import ConcurrentTestsRule;
        
        /**
         * Concurrent tests examples
         */
        public class ExampleTest {
        
            /**
             * Create a new TestRule that will be applied to all tests
             */
            @Rule
            public ConcurrentTestsRule ct = ConcurrentTestsRule.silentTests();
        
            /**
             * Tests using 10 threads and make 20 requests. This means until 10 simultaneous requests.
             */
            @Test
            @ConcurrentTest(requests = 20, threads = 10)
            public void testConcurrentExecutionSuccess(){
                Assert.assertTrue(true);
            }
        
            /**
             * Tests using 10 threads and make 20 requests. This means until 10 simultaneous requests.
             */
            @Test
            @ConcurrentTest(requests = 200, threads = 10, timeoutMillis = 100)
            public void testConcurrentExecutionSuccessWaitOnly100Millissecond(){
            }
        
            @Test(expected = RuntimeException.class)
            @ConcurrentTest(requests = 3)
            public void testConcurrentExecutionFail(){
                throw new RuntimeException("Fail");
            }
        }
        

        这是一个开源项目。随意改进。

        【讨论】:

          【解决方案10】:

          有什么问题:

          @Test
          void itWorks() {
              // stuff
          }
          
          @Test
          void itWorksRepeatably() {
              for (int i = 0; i < 10; i++) {
                  itWorks();
              }
          }
          

          与您测试每个值数组的情况不同,您并不特别关心哪个运行失败。

          无需在配置或注释中做你可以在代码中做的事情。

          【讨论】:

          • 我想像普通单元测试一样运行几个测试,并获得每个测试的跟踪和状态。
          • 在这种情况下,“@Before”s 和“@After”s 将不会运行
          • 这与在itWorks() 之前手动调用@Before 注释方法一起解决了我的问题。
          • 你知道 DRY 的概念吗? en.wikipedia.org/wiki/Don%27t_repeat_yourself 我建议进行一些设置,而不是到处复制粘贴循环。
          • 此答案的编辑队列已满;因此,我将其放在评论中:对于 JUnit4,测试需要公开。
          【解决方案11】:

          这对我来说更容易。

          public class RepeatTests extends TestCase {
          
              public static Test suite() {
                  TestSuite suite = new TestSuite(RepeatTests.class.getName());
          
                  for (int i = 0; i < 10; i++) {              
                  suite.addTestSuite(YourTest.class);             
                  }
          
                  return suite;
              }
          }
          

          【讨论】:

          • 太棒了,因为它不使用其他框架并且实际上与 JUnit 3 一起使用(对 android 至关重要)
          • 一个 JUnit4 的实现可以用一个 Runner 来完成:public class RepeatRunner extends BlockJUnit4ClassRunner { public RepeatRunner(Class klass) throws InitializationError { super(klass); } @Override public void run(final RunNotifier notifier) { for (int i = 0; i &lt; 10; i++) { super.run(notifier); } } } 虽然至少在 Eclipse JUnit 插件中你会得到类似的结果:“10/1 测试通过”
          猜你喜欢
          • 1970-01-01
          • 2016-10-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-12-20
          • 2017-02-15
          • 2019-08-29
          • 1970-01-01
          相关资源
          最近更新 更多