即使在测试超时并失败后,runClasses()
似乎也不会终止正在测试的代码。
SSCCE:
import org.junit.Test;
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
class Main {
public static void main(String[] args) {
Class<?>[] classes = { PublicTest.class };
Result result = JUnitCore.runClasses(classes);
System.out.println("Num tests: " + result.getRunCount());
System.out.println("Time: " + result.getRunTime());
System.out.println("Failure: " + result.getFailures().get(0));
}
}
测试类:
import org.junit.Test;
public class PublicTest {
@Test(timeout=10)
public void loop() {
while(true) {}
}
}
如何让代码终止?
答案 0 :(得分:2)
JUnitCore在某些时候确实创建了一个永远不会完成的新线程。 JUnit的main()实现避免在完成其工作时调用System.exit()而卡住。这对我来说似乎有些不整齐,但对你来说可能是最好的解决方案。
答案 1 :(得分:1)
JUnitCore.runMainAndExit(new RealSystem(), DefaultTest.class.getName(), PublicTest.class.getName());
效果很好,测试失败
答案 2 :(得分:1)
您不能将包级别类用作JUnit测试。这解释了行为的差异。如果我将DefaultTest
定义为包类,那么我从JUnit [*]得到以下错误:
Test class should have exactly one public constructor.
如果我定义了一个构造函数,那么我得到
DefaultTest should be public.
JUnit测试需要公开,因为JUnit需要实例化类。
因此,如果我有公共DefaultTest和公共PublicTest,那么线程的都不会被终止。这是JUnit的正常行为。它calls Thread#interrupt()(参见evaluateStatement())。但是你的测试没有注意到这一点。 JUnit不强制终止线程,因此线程继续运行。有关原因的解释,请参阅Thread#stop()。
因此,您的测试很好地停止,您需要他们的合作,例如:
@Test(timeout=10)
public void publicLoop() {
while(!Thread.interrupted()) {
}
}
这显然可能会对您的测试代码产生影响。对不起,我没有一个简单的解决方案。
[*]将以下行添加到main():
System.out.println("failures=" + result.getFailures());
答案 3 :(得分:1)
以下代码有助于:
@SuppressWarnings("deprecation")
private static void killStillRunningJUnitTestcaseThreads() {
Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
for (Thread thread : threadSet) {
if (!(thread.isDaemon())) {
final StackTraceElement[] threadStackTrace = thread.getStackTrace();
if (threadStackTrace.length > 1) {
StackTraceElement firstMethodInvocation = threadStackTrace[threadStackTrace.length - 1];
if (firstMethodInvocation.getClassName().startsWith("org.junit")) {
// HACK: must use deprecated method
thread.stop();
}
}
}
}
}
可以在Result result = JUnitCore.runClasses(classes);
Java 8流API样式中的相同内容:
@SuppressWarnings("deprecation")
private static void killStillRunningJUnitTestcaseThreads() {
Thread.getAllStackTraces().keySet().stream()
.filter(thread -> !(thread.isDaemon()))
.filter(thread -> Arrays.stream(thread.getStackTrace())
.reduce((__, lastOnly) -> lastOnly)
.map(StackTraceElement::getClassName)
.map(className -> className.startsWith("org.junit"))
.orElse(false))
.forEach(Thread::stop);
}
答案 4 :(得分:0)
我知道这是不久前的事情,但几天前我开始学习JUnit并偶然发现了同样的问题。我需要能够捕获无限循环,因此超时注释参数是部分解决方案,但我需要包含无限循环的测试用例来完成执行 - 而不是挂起。所以我想出了这个:
public class TestCase1 {
@Test(timeout=100)
public void test() {
System.out.println("Test Case called");
Thread myThread = new Thread() {
public void run() {
System.out.println("New thread created!");
new InfiniteLoop().runLoop();
}
};
myThread.start();
try {
Thread.sleep(101);
}
catch(InterruptedException e) {
}
finally {
myThread.stop();
}
}
}
基本上只是启动你在自己的线程中测试的方法,在定义的超时上休眠1毫秒(所以你确保测试由于超时而失败),捕获JUnit抛出的中断异常然后终止包含无限循环的线程。希望这有助于某人。
干杯, 周杰伦。
答案 5 :(得分:0)
强制线程停止不是这里的答案。你最终可能会留下昂贵的资源。例如。如果您的测试方法实际上产生了另一个进程,那么该进程将保持运行。你需要编写你的测试以便中断。
当超时到期时,JUnit会中断执行测试的线程。这本身并不会导致线程停止,因为Java中的整个中断机制需要合作。它旨在作为一种手段,要求阻止或长时间运行的操作比其他情况更早停止。
Thread
的一些阻止方法 - 例如Thread.sleep
和Thread.wait
- 会注意到它们已被中断,并会在发生这种情况时抛出InterruptedException
。您的代码可以捕获该异常并执行它想要的操作。如果它没有,它将像任何其他未捕获的异常一样退出,在这种情况下,JUnit将捕获它并报告您的测试因意外异常而失败(当然,除非您已配置你的测试期待那个例外)。 [在超时测试的情况下,JUnit实际上不会报告未捕获的异常 - 尽管它仍会捕获它。相反,它会报告超时。]
如果您不在方法中执行Thread.sleep
或Thread.wait
之类的操作,那么当JUnit中断线程时,您永远不会得到InterruptedException
。但是你不需要知道一个线程被中断了。只需调用Thread#isInterrupted来查找(这有清除线程中断状态的副作用)。然后,您可以采取清理和退出所需的任何步骤。或者不是 - 您是否合作是您的选择!
例如,您可以在无限循环示例中执行类似的操作:
import org.junit.Test;
public class PublicTest {
@Test(timeout=10)
public void loop() {
while(true) {
if (Thread.currentThread().isInterrupted()) {
break;
}
}
}
}