JUnit tearDown()在测试完成之前完成执行(使用自定义运行器)

时间:2014-07-06 08:11:36

标签: java multithreading junit jira-rest-java-api

JUnit @AfterClass方法在我的@Tests(自定义运行器)

之前完成执行

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

我已经浏览了google,彻底了解了JUnit文档,了解了解 StackOverflow上的JUnit执行顺序等,但我似乎无法找到任何其他这种情况的例子(可能是一个线程问题)。我在SpringJUnit4ClassRunner JavaDoc找到了一个使用自己的跑步者的示例,但它并没有帮助我解决问题。

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. 方法无序执行。
  3. 如果我在CustomRunner中的withAfterClasses()方法中添加一个Thread.sleep(3000),我总是得到三种可能性中的第一种。我可以用不同的睡眠强制三种可能性中的任何一种,例如,将一个放在tearDown()本身会强制第三。但是,Thread.sleep()并不是一个真正的解决方案。

    是否会以正确的顺序确定性地执行整个TestBugReporter1方法?如果是这样,我该怎么做?如果你知道一个更好的解决方案而不是把所有东西放在一个线程上,请说出来:)

    提前感谢您的帮助,不要复制粘贴链接,也不要解释它们的含义。我的多线程背景非常有限,所以我可能不了解没有上下文的文档。

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

2 个答案:

答案 0 :(得分:3)

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

Statement

  

表示在运行期间要在运行时执行的一个或多个操作   运行JUnit测试套件。

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

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

<强>更新

标准RunAfters实现很难在调用@AfterClass之前或在@AfterClass方法之后进行子类化和执行操作,所以我之前的建议并不那么容易实施。

这是你如何做到的。

CustomRunner

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;
    }
}

CustomRunAfters

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公共许可证)等相同的许可下许可我的修改。

答案 1 :(得分:2)

处理按正确顺序完成。

问题在于输出。 使用System.err打印断言错误时,您的输出将写入System.out。

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

另见