有没有办法在每次测试 class 后报告挥之不去的线程? (junit/maven)

Is there a way to report lingering threads after every test class? (junit/maven)

在一个写得不好的遗留代码库中,单元测试调用的代码会启动永不停止的线程。在大多数情况下它没有效果,但在某些情况下它会大大减慢构建速度,在其他情况下它会在构建过程中导致同一项目中的测试之间产生完全不清楚的副作用:例如测试 A 启动线程,然后 B 在同一个 JVM 中运行并以某种未定义的方式中断(解决方法是让 A 停止该线程)。

是否有一些工具可以与 junit 结合使用,以便在每次测试(或一个 class 中的一组测试)结束时,如果还有剩余线程,则测试失败 运行?

这将使我们能够快速识别并修复所有现有案例,同时防止以这种方式编写新测试。

public class FailOnLingeringThreadsTestBase
{
    private static Set<Thread> threadsBefore;

    @BeforeClass
    public static void takePhoto()
    {
        threadsBefore = Collections.unmodifiableSet(Thread.getAllStackTraces().keySet());
    }

    @AfterClass
    public static void spotTheDiffs()
    {
        Set<Thread> threadsAfter = Thread.getAllStackTraces().keySet();
        if (threadsAfter.size() != threadsBefore.size())
        {
            threadsAfter.removeAll(threadsBefore);
            throw new IllegalStateException("Lingering threads in test: " + threadsAfter);
        }
    }
}

显然 Maven/Surefire 允许您使用配置挂接侦听器!这是一种通过将检查作为 org.junit.runner.notification.RunListener.

的一部分来实现垂直集成检查的现实方法。

这是我现在使用的:

public class FailOnLingeringThreadsRunListener extends org.junit.runner.notification.RunListener
{
    private Set<Thread> threadsBefore;

    @Override
    public synchronized void testRunStarted(Description description) throws Exception
    {
        threadsBefore = takePhoto();
        super.testRunStarted(description);
    }

    @Override
    public synchronized void testRunFinished(Result result) throws Exception
    {
        super.testRunFinished(result);

        Set<Thread> threadsAfter = spotTheDiffs(threadsBefore);

        // only complain on success, as failures may have caused cleanup code not to run...
        if (result.wasSuccessful())
        {
            if (!threadsAfter.isEmpty())
                throw new IllegalStateException("Lingering threads in test: " + threadsAfter);
        }
    }

    public static Set<Thread> takePhoto()
    {

        return Collections.unmodifiableSet(Thread.getAllStackTraces().keySet());
    }

    @AfterClass
    public static Set<Thread> spotTheDiffs(Set<Thread> threadsBefore)
    {
        Set<Thread> threadsAfter = Thread.getAllStackTraces().keySet();
        if (threadsAfter.size() != threadsBefore.size())
        {
            threadsAfter.removeAll(threadsBefore);
            return Collections.unmodifiableSet(threadsAfter);
        }

        return Collections.emptySet();
    }
}

以下是我在构建中启用它的方法:

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <properties>
                        <property>
                            <name>listener</name>
                            <value>testutil.FailOnLingeringThreadsRunListener</value>
                        </property>
                    </properties>
                </configuration>
            </plugin>
...