以前は親クラスで使えるようにする方法 を書いたんだけど,
「それってやっぱり違うよな」って思ったので,
今度はRunnerを拡張する方法を紹介.
手順としては
- JMockアサーションを呼び出す独自Runnerの作成
- JMockアサーションの独自Statementの作成
まずは,Spring TestのSpringJUnit4ClassRunnerを
拡張したクラスを作成する.
public class JMockSpring3JUnit4ClassRunner
extends SpringJUnit4ClassRunner {
private Field mockeryField; // Mockeryインスタンスのフィールド
public JMockSpring3JUnit4ClassRunner(Class<?> clazz)
throws InitializationError {
super(clazz);
// Mockeryインスタンスを探してアクセス可能にする
mockeryField = findMockeryField(clazz);
mockeryField.setAccessible(true);
}
private static Field findMockeryField(Class<?> clazz)
throws InitializationError {
// JMock.classのソースからそのままパクリ
// Mockeryインスタンスのフィールドを探して返す
}
@Override
protected Statement withAfters(
FrameworkMethod frameworkMethod,
Object testInstance,
Statement statement) {
// まずは親クラスの処理を呼び出してStatementチェーンを作る
statement = super.withAfters(frameworkMethod, testInstance, statement);
// 今回のアサーションStatementをチェーンに追加する
return new VerifyJMockExpectations(testInstance, statement,
this.getExpectedException(frameworkMethod), this.mockeryField);
}
}
続いて,Runnerクラスで使っているJMockアサーション用の
Statementクラスを作成する.
public class VerifyJMockExpectations extends Statement {
private final Object test;
private final Statement next;
private final Class<? extends Throwable> expectedException;
private final Field mockeryField;
public VerifyJMockExpectations(Object testInstance,
Statement stmt,
Class<? extends Throwable> expectedException,
Field mockeryField) {
this.test = testInstance;
this.next = stmt;
this.expectedException = expectedException;
this.mockeryField = mockeryField;
}
@Override
public void evaluate() throws Throwable {
try {
// 次のチェーンを評価する
this.next.evaluate();
// 以下のコードも基本的にはJMock.classから流用
// 取得したMockeryインスタンスをアサートする
this.mockeryOf(this.test).assertIsSatisfied();
} catch (InvocationTargetException e) {
Throwable actual = e.getTargetException();
Class<? extends Throwable> expectedType = this.expectedException;
if (expectedType != null && expectedType.isInstance(actual)) {
// 例外が投げられるテストの場合の手当て
this.mockeryOf(this.test).assertIsSatisfied();
}
throw e;
}
}
private Mockery mockeryOf(Object test) {
// JMock.classのソースからそのままパクリ
// Mockeryインスタンスを取得する
}
}
これで普通にRunnerを@RunWithにわたせばOK.
@RunWith(JMockSpring3JUnit4ClassRunner.class)
@ContextConfiguration
public class RunnerTest {
private Mockery context; // モックコンテキストの定義も忘れずに!
// 以下,JMock+SpringTestのテストコード
}
やっぱりこっちの方がスマートな感じだね.
2010/05/02 Updated
evaluateメソッドのコードに転記ミスがあったので修正.
ついでに説明コメントを一応追加してみた.