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!
我认为这是一个线程问题,因为:
如果我在CustomRunner中的withAfterClasses()方法中添加一个Thread.sleep(3000),我总是得到三种可能性中的第一种。我可以用不同的睡眠强制三种可能性中的任何一种,例如,将一个放在tearDown()本身会强制第三。但是,Thread.sleep()并不是一个真正的解决方案。
是否会以正确的顺序确定性地执行整个TestBugReporter1方法?如果是这样,我该怎么做?如果你知道一个更好的解决方案而不是把所有东西放在一个线程上,请说出来:)
提前感谢您的帮助,不要复制粘贴链接,也不要解释它们的含义。我的多线程背景非常有限,所以我可能不了解没有上下文的文档。
注意:TestNG有一个单线程配置,但我们现有的测试套件是纯JUnit,所以它不是一个选项。
答案 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");
}
}
在上面的代码中,您可以使用beforeRunAfters
和afterRunAfters
方法执行自定义操作。
注意:上面的代码片段包含来自JUnit 4.5的修改后的源代码。在这是必要的情况下,我特此在与JUnit(Eclipse公共许可证)等相同的许可下许可我的修改。
答案 1 :(得分:2)
处理按正确顺序完成。
问题在于输出。 使用System.err打印断言错误时,您的输出将写入System.out。
他们的输出不一定是正确的顺序。
另见