一遍又一遍地运行相同的junit测试的简单方法?


121

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

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

我们已经有一个复杂的方法来执行此操作,但是我正在寻找一种简单的方法来执行此操作,以便可以确保我一直试图修复的不稳定测试保持不变。

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


5
我很好奇您为什么要这么做。
2009年

我正在运行一个大型黑匣子测试,做了一个小的更改,并希望了解它如何影响此以前不稳定的测试的稳定性。
Stefan Thyberg 09年

的确如此,只是您希望它一直运行到失败,而我只想多次运行它,这可能会影响我得到的答案。
Stefan Thyberg,2010年

4
您是否反对TestNG,因为如果不这样做,那么您可以只使用@Test(invocationCount = 10),这就是全部。
罗伯特·马赛奥利

1
我不是“反对” TestNG,我们只是没有在那个项目中使用它。
Stefan Thyberg,2011年

Answers:


123

最简单的方法(至少需要最少的新代码)是将测试作为参数化测试运行(用 @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次。

请注意,还有其他方法可以潜在地解决此问题(例如“理论”亚军),但它们都需要亚军。不幸的是,JUnit当前不支持跑步者层。那是一个将其他跑步者拴在一起的跑步者。


2
不幸的是,我已经在和另一个跑步者一起运行@RunWith,但是否则这将是一个理想的解决方案。
Stefan Thyberg 09年

是的,这是我想要的解决方案,对大多数人来说这将是最好的解决方案,因此,我将继续接受答案。
Stefan Thyberg,2009年

有关替代的且可能不太hacky的解决方案,请参见:stackoverflow.com/a/21349010/281545
Mr_and_Mrs_D

不错的解决方案!我有一个异常告诉我数据方法应该返回数组的Iterable。我相应地修复了它:@ Parameterized.Parameters public static Iterable <Object []> data(){return Arrays.asList(new Object [20] [0]); }
nadre '18

1
您能否链接到JUnit 5的此答案?它描述了请求的功能,该功能已添加到JUnit 5
Marcono1234 '18

100

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

在此处输入图片说明

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

示例运行624次测试10次: 在此处输入图片说明


4
太完美了,现在,如果您能指出一种偏食的方式,那就可以回答OP的问题了
卡尔

依靠特定工具来承载实际逻辑或要求是一种反模式。
Mickael

1
@Mickael N次重复测试通常不是测试的要求。实际上,测试应该是确定性的,因此无论重复多少次,测试都应始终产生相同的结果。您能解释一下您所说的反模式吗?
smac89

如果重复测试对一名开发人员有用,那么对其他开发人员也可能有用。因此,如果测试运行时和代码可以托管允许重复的逻辑,则应首选它,因为它可以分解工作量和解决方案,并允许贡献者使用所需工具获得相同结果。当可重用逻辑可以放入代码中时,将其放置在IDE /开发人员区域中是一种缺失的因式分解。
Mickael

68

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

@Repeat(value = 10)

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


46
改变测试框架并不是我所谓的简单方法。
Stefan Thyberg 09年

3
您无需更改测试框架-它可以与JUnit正常工作。主要缺点是JUnit仍将其视为单个测试。因此,第一次中断时,执行将停止。但是,如果您尚未使用Spring,则可能不是您想要的方式...
tveon 2015年

似乎不适合我(通过Spring Boot 1.5.1春季版4.3.6)
David Tonhofer

在Spring Boot 2.1.6和
Junit

与spring boot 2完美配合。不要忘了按照海报的“ spring单元测试”链接添加@RunWith(SpringRunner :: class)!
Agoston Horvath,

33

受到以下资源的启发:

创建并使用@Repeat注释,如下所示:

public class MyTestClass {

    @Rule
    public RepeatRule repeatRule = new RepeatRule();

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

Repeat.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;
    }
}

动力模拟

将此解决方案与配合使用@RunWith(PowerMockRunner.class),需要更新至Powermock 1.6.5(包括一个补丁)。


是。您如何执行测试?
R. Oosterholt 2015年

我自己没有用食。也许您没有使用junut 4测试跑步机?(请参阅文档“自定义测试配置”
R. Oosterholt

29

使用JUnit 5,我可以使用@RepeatedTest批注解决此问题:

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

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


听起来非常有前途,只需注意还没有发行版本。
9ilsdx 9rvj 0lo

1
几周前,JUnit 5投放市场。
mkobit

11

出现任何问题:

@Test
void itWorks() {
    // stuff
}

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

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

无需在配置或注释中执行您可以在代码中执行的操作。


2
我想将几个测试作为正常的单元测试运行,并获取每个测试的跟踪和状态。
Stefan Thyberg,2009年

24
在这种情况下,“ @ Before”和“ @After”将不会运行
Bogdan

3
@BeforeitWorks() 解决我的问题之前,这需要手动调用带注释的方法。
若昂·内维斯

您知道DRY概念吗?zh_cn.wikipedia.org/wiki/Don%27t_repeat_yourself我建议您进行一些设置,而不是将循环复制粘贴到任何地方。
奇奇瓦

该答案的编辑队列已满;因此,我将其发表评论:对于JUnit4,测试需要公开。
理查德·杰索普

7

这对我来说更容易。

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的实际工作(至关重要的机器人)
弗拉基米尔·伊万诺夫

1
可以使用Runner来实现JUnit4的实现: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 < 10; i++) { super.run(notifier); } } }尽管至少在Eclipse JUnit插件中,您仍会 获得以下结果:“通过了10/1测试”
Peter Wippermann

7

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++;
   }
}

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


是的,这里是同样的问题,已经在使用另一名跑步者,所以不能使用这个好主意。
Stefan Thyberg,2010年

是的,我在RunWith上也遇到了同样的问题。。。随着时间的流逝,我对tempus-fugit进行了一些调整,以便在需要反复运行时可以使用@Rule而不是Runner。您可以使用@Repeating而不是间歇性地对其进行标记。规则版本不会运行@ Before / @ Afters。有关更多详细信息,请参见tempus-fugit.googlecode.com/svn/site/documentation/…(向下滚动以进行加载/浸泡测试)。
托比,2010年

0

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

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

这是一个开源项目。随时改善。


0

您可以从主要方法运行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

            }
        }

    }

}

0

这基本上是上面由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() { }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.