2

JUnit @AfterClass 方法在我的 @Tests (自定义运行程序)之前完成执行

我正在尝试制作自己的 JUnit 测试运行器。每次执行 @AfterClass 注释的方法时,我都想做一些事情。根据 JUnit 文档,@AfterClass 方法应该在所有测试运行后开始执行。

我浏览了谷歌,彻底阅读了 JUnit 文档,阅读了 StackOverflow 上的 JUnit 执行顺序等信息,但我似乎找不到任何其他发生这种情况的例子(可能是线程问题)。我在SpringJUnit4ClassRunner JavaDoc中找到了一个使用他们自己的跑步者的示例 Spring,但它并没有帮助我解决问题。

public class CustomRunner extends BlockJUnit4ClassRunner {

    public CustomRunner (Class<?> klass) throws InitializationError {
        super(klass);
    }

    @Override
    protected Statement withAfterClasses(Statement statement) {
        Statement s = super.withAfterClasses(statement);
        System.out.println("tearDown() executed!");
        return s;
    }
}

没有 CustomRunner:

public class TestBugReporter0 {

    @Before
    public void setUp() {}

    @Test
    public void testAssertionFailure() {
        assertEquals(3, 2);
    }

    @AfterClass
    public static void tearDown() {
        System.out.println("tearDown() executed!");
    }
}

使用 CustomRunner:

@RunWith(CustomRunner.class)
public class TestBugReporter1 {

    @Before
    public void setUp() {}

    @Test
    public void testAssertionFailure() {
        assertEquals(3, 2);
    }

    @AfterClass
    public static void tearDown() {}
}

TestBugReporter0总是打印出以下内容:

setUp() executed!
java.lang.AssertionError: Expected :3, Actual :2
tearDown() executed!

但是,TestBugReporter1 不是确定性的。运行 20 次后,我总是得到以下三个之一。

tearDown() executed!
setUp() executed!
java.lang.AssertionError: Expected :3, Actual :2

java.lang.AssertionError: Expected :3, Actual :2
tearDown() executed!
setUp() executed!

setUp() executed!
java.lang.AssertionError: Expected :3, Actual :2
tearDown() executed!

我认为这是一个线程问题,因为:

  1. 结果是不确定的。
  2. 这些方法无序执行。

如果我在 CustomRunner 中将 Thread.sleep(3000) 添加到我的 withAfterClasses() 方法中,我总是会得到三种可能性中的第一种。我可能会强制使用不同睡眠的三种可能性中的任何一种,例如,将一个放入 tearDown() 本身会强制使用第三个。但是, Thread.sleep() 并不是真正的解决方案。

会让整个 TestBugReporter1 方法以正确的顺序确定地执行吗?如果是这样,我该怎么做?如果您知道比将所有内容都放在一个线程上更好的解决方案,请说出来!:)

提前感谢您的帮助,感谢您在不解释链接含义的情况下复制粘贴链接。我的多线程背景非常有限,所以如果没有上下文,我可能无法理解文档。

注意:TestNG 有一个单线程配置,但我们现有的测试套件是纯 JUnit,所以它不是一个选项。

4

2 回答 2

3

该方法withAfterClasses(以及以 开头的任何其他方法with不执行测试类中的任何内容。相反,它们构建了一个对象链,这些Statement对象代表稍后将要执行的内容。

Statement

表示在运行 JUnit 测试套件过程中要在运行时执行的一项或多项操作。

查看类RunAfters,它是 的子类,Statement由 的默认实现返回withAfterClasses

似乎 JUnit 可以Statement在测试类的生命周期中的不同时间请求这个;但是你不应该关心这一点 - 只需返回一个实例Statement。您可以将其视为命令模式。

更新

标准实现使得子类化和在调用方法之前或之后RunAfters做一些事情变得困难,所以我之前的建议并不容易实现。@AfterClass@AfterClass

这是你如何做到的。

自定义跑步者

public class CustomRunner extends BlockJUnit4ClassRunner {
    public CustomRunner(Class<?> klass) throws InitializationError {
        super(klass);
    }

    @Override
    protected Statement withAfterClasses(Statement statement) {
        List<FrameworkMethod> afters = getTestClass().getAnnotatedMethods(AfterClass.class);
        statement = new CustomRunAfters(statement, afters, null);
        return statement;
    }
}

自定义运行后

public class CustomRunAfters extends Statement {

    private final Statement fNext;

    private final Object fTarget;

    private final List<FrameworkMethod> fAfters;

    public CustomRunAfters(Statement next, List<FrameworkMethod> afters, Object target) {
        fNext = next;
        fAfters = afters;
        fTarget = target;
    }

    @Override
    public void evaluate() throws Throwable {
        List<Throwable> fErrors = new ArrayList<Throwable>();
        fErrors.clear();
        try {
            fNext.evaluate();
        } catch (Throwable e) {
            fErrors.add(e);
        } finally {
            beforeRunAfters();
            for (FrameworkMethod each : fAfters) {
                try {
                    each.invokeExplosively(fTarget);
                } catch (Throwable e) {
                    fErrors.add(e);
                }
            }
            afterRunAfters();
        }
        if (fErrors.isEmpty())
            return;
        if (fErrors.size() == 1)
            throw fErrors.get(0);
        throw new MultipleFailureException(fErrors);
    }

    private void afterRunAfters() {
        System.err.println("After running @AfterClass methods");
    }

    private void beforeRunAfters() {
        System.err.println("Before running @AfterClass methods");
    }
}

在上面的代码中,您可以在beforeRunAftersafterRunAfters方法中执行您的自定义操作。

注意:上面的代码片段包含来自 JUnit 4.5 的修改后的源代码。在必要的情况下,我特此根据与 JUnit(Eclipse 公共许可证)等相同的许可证许可我的修改。

于 2014-07-06T08:32:27.560 回答
2

处理以正确的顺序完成。

问题是输出。您的输出写入 System.out,而断言错误使用 System.err 打印。

他们的输出不一定是正确的顺序。

也可以看看

于 2014-07-06T08:33:11.860 回答