如何在 JUnit 4.12 中组合 @Rule 和 @ClassRule
How to combine @Rule and @ClassRule in JUnit 4.12
根据the 4.12 release notes,可以使用@Rule 和@ClassRule 注释测试class 的静态成员:
a static member annotated with both @Rule and @ClassRule is now considered valid. This means a single rule may be used to perform actions both before/after a class (e.g. setup/tear down an external resource) and between tests (e.g. reset the external resource),
我想使用此功能在文件中的所有测试开始时初始化资源,在每次测试之间对资源进行一些清理,并在所有测试完成后处理它。此资源当前由扩展 ExternalResource 的 class 表示。
在我的 before
和 after
方法中,如何区分 "before/after all tests" 和 "before/after each test"?我是否需要使用 TestRule
的 different/custom 实现来完成此操作?
注释为@Before
和@After
的方法每次测试前后为运行,注释为@BeforeClass
和@AfterClass
的方法为运行 分别在 class 中的 first/last 测试前后。
@Rule
的before/after方法在每次测试前后执行,而@ClassRule
的before/after方法是运行before/after整个测试class.
您可以将 ExternalResource 用于 @Rule
或 @ClassRule
情况,只要处理程序方法对这两种情况都能做出正确反应。据我从文档中可以看出,没有办法在规则 class 方法中区分两个规则类别。如果您对这两种情况都使用规则 class,则将对两者应用相同的规则。
您无法区分 @BeforeClass
和 @Before
或 @AfterClass
和 @After
。有关添加此功能的原因的更多详细信息,请参阅 pull request。
您可以创建自己的规则来实现 TestRule
和 MethodRule
:
public SharableExternalResource implements TestRule, MethodRule {
public final Statement apply(
final Statement base, Description description) {
return
new Statement() {
@Override
public void evaluate() throws Throwable {
beforeClass();
List<Throwable> errors = new ArrayList<Throwable>();
try {
base.evaluate();
} catch (Throwable t) {
errors.add(t);
} finally {
try {
afterClass();
} catch (Throwable t) {
errors.add(t);
}
}
MultipleFailureException.assertEmpty(errors);
}
};
}
public final Statement apply(
Statement base, FrameworkMethod method, Object target) {
return
new Statement() {
@Override
public void evaluate() throws Throwable {
before();
List<Throwable> errors = new ArrayList<Throwable>();
try {
base.evaluate();
} catch (Throwable t) {
errors.add(t);
} finally {
try {
after();
} catch (Throwable t) {
errors.add(t);
}
}
MultipleFailureException.assertEmpty(errors);
}
};
}
public void beforeClass() throws Exception {
// do nothing
}
protected void before() throws Exception {
// do nothing
}
protected void after() throws Exception {
// do nothing
}
public void afterClass() throws Exception {
// do nothing
}
}
您可以实施 TestRule#apply
并使用 Description
的 isTest
和 isSuite
方法来确定哪种 Statement
您的 TestRule
正在应用于。
这是一个示例界面,您可以构建该界面以提供具有完整 before
、after
、verify
、beforeClass
、afterClass
、verifyClass
类型行为:
public interface CombinedRule extends TestRule {
default Statement apply(Statement base, Description description) {
if (description.isTest()) {
return new Statement() {
public void evaluate() throws Throwable {
before();
try {
base.evaluate();
verify();
} finally {
after();
}
}
};
}
if (description.isSuite()) {
return new Statement() {
public void evaluate() throws Throwable {
beforeClass();
try {
base.evaluate();
verifyClass();
} finally {
afterClass();
}
}
};
}
return base;
}
default void before() throws Exception {
//let the implementer decide whether this method is useful to implement
}
default void after() {
//let the implementer decide whether this method is useful to implement
}
/**
* Only runs for Tests that pass
*/
default void verify() {
//let the implementer decide whether this method is useful to implement
}
default void beforeClass() throws Exception {
before();
}
default void afterClass() {
after();
}
/**
* Only runs for Suites that pass
*/
default void verifyClass() {
verify();
}
}
根据the 4.12 release notes,可以使用@Rule 和@ClassRule 注释测试class 的静态成员:
a static member annotated with both @Rule and @ClassRule is now considered valid. This means a single rule may be used to perform actions both before/after a class (e.g. setup/tear down an external resource) and between tests (e.g. reset the external resource),
我想使用此功能在文件中的所有测试开始时初始化资源,在每次测试之间对资源进行一些清理,并在所有测试完成后处理它。此资源当前由扩展 ExternalResource 的 class 表示。
在我的 before
和 after
方法中,如何区分 "before/after all tests" 和 "before/after each test"?我是否需要使用 TestRule
的 different/custom 实现来完成此操作?
注释为@Before
和@After
的方法每次测试前后为运行,注释为@BeforeClass
和@AfterClass
的方法为运行 分别在 class 中的 first/last 测试前后。
@Rule
的before/after方法在每次测试前后执行,而@ClassRule
的before/after方法是运行before/after整个测试class.
您可以将 ExternalResource 用于 @Rule
或 @ClassRule
情况,只要处理程序方法对这两种情况都能做出正确反应。据我从文档中可以看出,没有办法在规则 class 方法中区分两个规则类别。如果您对这两种情况都使用规则 class,则将对两者应用相同的规则。
您无法区分 @BeforeClass
和 @Before
或 @AfterClass
和 @After
。有关添加此功能的原因的更多详细信息,请参阅 pull request。
您可以创建自己的规则来实现 TestRule
和 MethodRule
:
public SharableExternalResource implements TestRule, MethodRule {
public final Statement apply(
final Statement base, Description description) {
return
new Statement() {
@Override
public void evaluate() throws Throwable {
beforeClass();
List<Throwable> errors = new ArrayList<Throwable>();
try {
base.evaluate();
} catch (Throwable t) {
errors.add(t);
} finally {
try {
afterClass();
} catch (Throwable t) {
errors.add(t);
}
}
MultipleFailureException.assertEmpty(errors);
}
};
}
public final Statement apply(
Statement base, FrameworkMethod method, Object target) {
return
new Statement() {
@Override
public void evaluate() throws Throwable {
before();
List<Throwable> errors = new ArrayList<Throwable>();
try {
base.evaluate();
} catch (Throwable t) {
errors.add(t);
} finally {
try {
after();
} catch (Throwable t) {
errors.add(t);
}
}
MultipleFailureException.assertEmpty(errors);
}
};
}
public void beforeClass() throws Exception {
// do nothing
}
protected void before() throws Exception {
// do nothing
}
protected void after() throws Exception {
// do nothing
}
public void afterClass() throws Exception {
// do nothing
}
}
您可以实施 TestRule#apply
并使用 Description
的 isTest
和 isSuite
方法来确定哪种 Statement
您的 TestRule
正在应用于。
这是一个示例界面,您可以构建该界面以提供具有完整 before
、after
、verify
、beforeClass
、afterClass
、verifyClass
类型行为:
public interface CombinedRule extends TestRule {
default Statement apply(Statement base, Description description) {
if (description.isTest()) {
return new Statement() {
public void evaluate() throws Throwable {
before();
try {
base.evaluate();
verify();
} finally {
after();
}
}
};
}
if (description.isSuite()) {
return new Statement() {
public void evaluate() throws Throwable {
beforeClass();
try {
base.evaluate();
verifyClass();
} finally {
afterClass();
}
}
};
}
return base;
}
default void before() throws Exception {
//let the implementer decide whether this method is useful to implement
}
default void after() {
//let the implementer decide whether this method is useful to implement
}
/**
* Only runs for Tests that pass
*/
default void verify() {
//let the implementer decide whether this method is useful to implement
}
default void beforeClass() throws Exception {
before();
}
default void afterClass() {
after();
}
/**
* Only runs for Suites that pass
*/
default void verifyClass() {
verify();
}
}