1

即使在测试超时并失败后,似乎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) {}
    }
}

如何让代码终止?

4

6 回答 6

2

JUnitCore 在某些时候确实确实创建了一个永远不会完成的新线程。JUnit 的 main() 实现通过在完成其工作时调用 System.exit() 来避免卡住。这对我来说似乎有点不整洁,但可能是你最好的解决方案。

于 2012-06-18T19:08:50.683 回答
1
JUnitCore.runMainAndExit(new RealSystem(), DefaultTest.class.getName(), PublicTest.class.getName());  

运行良好,测试失败

于 2012-06-18T19:23:15.977 回答
1

您不能将包级别的类用作 JUnit 测试。这解释了行为上的差异。如果我定义DefaultTest为一个包类,那么我会从 JUnit[*] 收到以下错误:

Test class should have exactly one public constructor.

如果我定义一个构造函数,那么我得到

DefaultTest should be public.

JUnit 测试需要公开,因为 JUnit 需要实例化类。

所以,如果我有 public DefaultTest 和 public PublicTest,那么两个线程都不会被终止。这是 JUnit 的正常行为。它调用 Thread#interrupt()(参见 evaluateStatement())。但是您的测试没有注意到这一点。JUnit 不会强制终止线程,因此线程会继续运行。有关原因的解释,请参阅Thread#stop()

所以让你的测试很好地停止,你需要他们的合作,例如:

@Test(timeout=10)
public void publicLoop() {
    while(!Thread.interrupted()) {
    }
}

这显然也可能对您的被测代码产生影响。对不起,我没有一个简单的解决方案。

[*] 将以下行添加到您的 main() 中:

System.out.println("failures=" + result.getFailures());
于 2012-06-18T21:54:23.427 回答
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);
}
于 2014-03-28T20:48:26.130 回答
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 抛出的中断异常,然后终止包含无限循环。希望这可以帮助某人。

干杯,杰伊。

于 2013-06-29T06:50:26.270 回答
0

强制线程停止不是这里的答案。您最终可能会留下无法回收的昂贵资源。例如,如果您的测试方法实际上产生了另一个进程,则该进程将继续运行。您需要编写可中断的测试。

当超时到期时,JUnit 会中断正在执行测试的线程。这本身并不会导致线程停止,因为 Java 中的整个中断机制都需要合作。它旨在作为一种请求阻塞或长时间运行的操作比其他方式更早停止的方法。

一些阻塞方法Thread- 比如Thread.sleepThread.wait- 会注意到它们已被中断并InterruptedException在发生这种情况时会抛出一个。您的代码可以捕获该异常并做它想做的事。如果没有,它将像处理任何其他未捕获的异常一样退出,在这种情况下,JUnit 会捕获它并报告您的测试由于意外异常而失败(当然,除非您已经配置了测试期待那个例外)。[在测试超时的情况下,JUnit 实际上不会报告未捕获的异常——尽管它仍然会捕获它。相反,它会报告超时。]

如果你没有在你的方法中做任何类似于Thread.sleep或的事情Thread.wait,那么你永远不会InterruptedException在 JUnit 中断线程时得到一个。但是您不需要它来意识到线程已被中断。只需调用 Thread#isInterrupted 即可查明(这有清除线程中断状态的副作用)。然后你可以采取任何必要的步骤来清理和退出。与否 - 是否合作由您选择!

例如,您可以在无限循环示例中执行以下操作:

import org.junit.Test;

public class PublicTest {
    @Test(timeout=10)
    public void loop() {
        while(true) {
           if (Thread.currentThread().isInterrupted()) {
               break;
           }
        }
    }
}
于 2015-11-04T20:04:41.980 回答