正如标题所说,我正在寻找一些简单的方法来使用 Eclipse 自动连续多次运行 JUnit 4.x 测试。
例如,连续运行 10 次相同的测试并报告结果。
我们已经有一种复杂的方法来做到这一点,但我正在寻找一种简单的方法来做到这一点,以便我可以确定我一直在尝试修复的不稳定测试保持不变。
一个理想的解决方案是我不知道的 Eclipse 插件/设置/功能。
执行此操作的最简单(因为需要最少数量的新代码)方法是将测试作为参数化测试运行(使用 @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 目前不支持运行层。那是一个链接其他跑步者的跑步者。
使用 IntelliJ,您可以从测试配置中执行此操作。打开此窗口后,您可以选择运行测试任意次数。
https://i.stack.imgur.com/WpeLy.png
当您运行测试时,intellij 将按照您指定的次数执行您选择的所有测试。
https://i.stack.imgur.com/lwFMI.png
我发现 Spring 的重复注释对这种事情很有用:
@Repeat(value = 10)
最新(Spring Framework 4.3.11.RELEASE API)文档:
org.springframework.test.annotation
春季单元测试
使用 JUnit 5,我可以使用 @RepeatedTest 注释解决这个问题:
@RepeatedTest(10)
public void testMyCode() {
//your test code goes here
}
请注意,@Test
注释不应与 @RepeatedTest
一起使用。
受以下资源启发:
博文
这个解决方案
评论版
例子
创建和使用 @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;
}
重复规则.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)。
有什么问题:
@Test
void itWorks() {
// stuff
}
@Test
void itWorksRepeatably() {
for (int i = 0; i < 10; i++) {
itWorks();
}
}
与测试每个值数组的情况不同,您并不特别关心哪个运行失败。
无需在配置或注释中执行您可以在代码中执行的操作。
itWorks()
解决我的问题之前手动调用 @Before
注释的方法一起。
这对我来说更容易。
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;
}
}
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 测试通过”
tempus-fugit 库中有一个间歇性注释,可与 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。
这基本上是 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() { }
}
我构建了一个允许进行此类测试的模块。但它的重点不仅在于重复。但是为了保证某些代码是线程安全的。
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");
}
}
这是一个开源项目。随意改进。
您可以从 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
}
}
}
}
不定期副业成功案例分享