TestRule、特に apply() を拡張する必要があります。例として、org.junit.rules.ExternalResource & org.junit.rules.TemporaryFolder を見てください。
ExternalResource は次のようになります。
public abstract class ExternalResource implements TestRule {
public Statement apply(Statement base, Description description) {
return statement(base);
}
private Statement statement(final Statement base) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
before();
try {
base.evaluate();
} finally {
after();
}
}
};
}
/**
* Override to set up your specific external resource.
* @throws if setup fails (which will disable {@code after}
*/
protected void before() throws Throwable {
// do nothing
}
/**
* Override to tear down your specific external resource.
*/
protected void after() {
// do nothing
}
}
次に、TemporaryFolder はこれを拡張し、before() と after() を実装します。
public class TemporaryFolder extends ExternalResource {
private File folder;
@Override
protected void before() throws Throwable {
// create the folder
}
@Override
protected void after() {
// delete the folder
}
したがって、before は testMethod の前に呼び出され、after は finally で呼び出されますが、次のように例外をキャッチしてログに記録できます。
private Statement statement(final Statement base) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
before();
try {
base.evaluate();
} catch (Exception e) {
log.error("caught Exception", e);
} finally {
after();
}
}
};
}
編集:次の作品:
public class SoTest {
public class ExceptionLoggingRule implements TestRule {
public Statement apply(Statement base, Description description) {
return statement(base);
}
private Statement statement(final Statement base) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
try {
base.evaluate();
} catch (Exception e) {
System.out.println("caught an exception");
e.printStackTrace(System.out);
throw e;
}
}
};
}
}
@Rule public ExceptionLoggingRule exceptionLoggingRule = new ExceptionLoggingRule();
@Rule public ExpectedException expectedException = ExpectedException.none();
@Test
public void testMe() throws Exception {
expectedException.expect(IOException.class);
throw new IOException("here we are");
}
}
テストに合格し、次の出力が得られます。
caught an exception
java.io.IOException: here we are
at uk.co.farwell.junit.SoTest.testMe(SoTest.java:40)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
...
ルールが適用される順序は、testMe メソッドを呼び出す ExceptionLoggingRule を呼び出す ExpectedException です。ExceptionLoggingRule は例外をキャッチし、ログに記録して再スローし、ExpectedException によって処理されます。
予期しない例外のみをログに記録する場合は、ルールの宣言順序を切り替えるだけです。
@Rule public ExpectedException expectedException = ExpectedException.none();
@Rule public ExceptionLoggingRule exceptionLoggingRule = new ExceptionLoggingRule();
このようにして、expectedException が最初に適用され (つまり、exceptionLoggingRule にネストされ)、予期されていない例外のみが再スローされます。さらに、何らかの例外が予想され、何も発生しなかった場合、expectedException は AssertionError をスローし、これもログに記録されます。
この評価順序は保証されていませんが、非常に異なる JVM で遊んだり、Test クラス間で継承したりしない限り、変化する可能性はほとんどありません。
評価順序が重要な場合は、いつでも一方のルールを他方のルールに渡して評価することができます。
編集: 最近リリースされた Junit 4.10 では、 @RuleChain を使用してルールを正しくチェーンできます。
public static class UseRuleChain {
@Rule
public TestRule chain= RuleChain
.outerRule(new LoggingRule("outer rule")
.around(new LoggingRule("middle rule")
.around(new LoggingRule("inner rule");
@Test
public void example() {
assertTrue(true);
}
}
ログを書き込みます
starting outer rule
starting middle rule
starting inner rule
finished inner rule
finished middle rule
finished outer rule