我想停止并标记为运行junit测试失败(在Maven 3版本中执行)。我知道有三种方法:
1)使用带有超时参数的测试注释:
@Test(timeout=100)
public void testWithTimeout() {
...
}
2)使用规则注释:
@Rule
public Timeout globalTimeout = new Timeout(100);
3)使用以下选项配置maven-surefire-plugin:
forkedProcessTimeoutInSeconds=1
reuseForks=false
线索是1)和2)需要改变每个测试(当你有数千个时它会受伤)。 3)解决方案是不可接受的,因为在许多模块中,第一次测试开始测试所使用的上下文 - 测试性能会急剧下降。
您对此有何其他想法?任何棘手的解决方案(不涉及修补JUnit :))?
答案 0 :(得分:2)
您可以尝试通过编写扩展BlockJunit4ClassRunner的类来定义自己的测试运行器,如果测试执行超出定义的超时,则会失败。 然后使用@RunWith(CustomizedTestRunner.class)注释您的测试类 您仍然需要修改类,但可以在单个位置指定超时值。
答案 1 :(得分:0)
实际上,超时规则对类中的所有测试方法应用相同的超时。
如果您不想将其添加到每个Test类,则可以在Test基类中定义一次。
对于不需要更改所有类的内容,您可以实现Junit自定义套件运行器(请参阅第二个代码示例)
只是为了好玩,使用弃用的Thread.stop方法来解决这个hacky解决方案的问题?
在函数启动每个测试的观察者线程之前,超时后会杀死Junit测试线程。
如果测试完成,清理会杀死观察者线程。
适用于这个小小的演示,不确定这是否适用于生产规模。
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
// You could also make this a base class for test classes
public class TimeoutTest {
Thread watcherThread ;
Thread junitTestThread;
final static int testTimeout = 2000;
@Before
public void myInit()
{
junitTestThread = Thread.currentThread();
watcherThread = new Thread()
{
@Override
public void run()
{
try {
Thread.sleep(testTimeout);
junitTestThread.stop();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
watcherThread.start();
}
@After
public void myCleanup() throws InterruptedException
{
watcherThread.stop();
}
@Test
public void testPassingFastTest() throws InterruptedException {
Thread.sleep(1000);
}
@Test
public void testFailingSlowTest() throws InterruptedException {
Thread.sleep(3000);
}
}
或者使用套件为几个测试类执行此操作:
import java.util.Arrays;
import org.junit.runner.Description;
import org.junit.runner.RunWith;
import org.junit.runner.notification.RunListener;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.RunnerBuilder;
@RunWith(AllTests.class)
public class AllTests extends Suite{
Thread watcherThread ;
Thread junitTestThread;
final static int testTimeout = 70;
public AllTests(final Class<?> clazz) throws InitializationError {
super(clazz, findClasses());
}
private static Class<?>[] findClasses() {
// You could write code here to get the list of all test classes from specific directories
return new Class<?>[] {TimeoutTest.class,TimeoutTest2.class};
}
@Override
public void run(final RunNotifier notifier) {
notifier.addListener(new RunListener() {
@Override
public void testStarted(final Description description) {
System.out.println("Before test " + description.getDisplayName());
junitTestThread = Thread.currentThread();
watcherThread = new Thread()
{
@Override
public void run()
{
try {
Thread.sleep(testTimeout);
junitTestThread.stop();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
watcherThread.start();
}
@Override
public void testFinished(final Description description) {
System.out.println("After test " + description.getDisplayName());
watcherThread.stop();
}
}
);
super.run(notifier);
}
}
答案 2 :(得分:0)
可能不是您正在寻找的解决方案,但如果您在连续构建服务器中运行测试,则通常会有一个全局超时的作业。 e.g. for Jenkins
答案 3 :(得分:0)
@Test
和@Rule
注释仅适用于JUnit4。对于JUnit3,您必须手动管理一个新线程来设置超时时间:
public void testWithTimeout() throws InterruptedException, TimeoutException {
final int factorialOf = 1 + (int) (30000 * Math.random());
System.out.println("computing " + factorialOf + '!');
Thread testThread = new Thread() {
public void run() {
System.out.println(factorialOf + "! = " + Utils.computeFactorial(factorialOf));
}
};
testThread.start();
Thread.sleep(1000);
testThread.interrupt();
if (testThread.isInterrupted()) {
throw new TimeoutException("the test took too long to complete");
}
}
来源和更多信息:https://netbeans.org/kb/docs/java/junit-intro.html#Exercise_24
或更紧凑的解决方案是将Callable
和Future
一起使用。此处的信息:How can I wrap a method so that I can kill its execution if it exceeds a specified timeout?