はじめに
JUnit+Mockitoで単体試験の際、とあるメソッドを実行の直前で状態を変化させ、変化させた後のメソッドの振る舞いを見たい、というケースがあったとします。
例えば以下のケースです。
- ファイルに関する処理を行う
- 次に当該ファイルに関する処理を行う寸前でファイルを削除
- ファイルが削除された後に実際のメソッドを呼ばれた場合の振る舞いを確認したい
このようなケースを確認する際、実際に処理を確認したい場合は、上記3の処理を行う手前でデバッガを止めて、上記2を手動で行い、その後処理を再開して確認することになります。
しかし、JUNITで処理を自動化したい場合はデバッガを止めるわけにはいきません。
そこで、上記3のメソッドを呼ばれた際に、当該メソッドをスタブ化し、スタブ化したメソッド内にて別の処理を行うことができる、という機能が、MockitoのthenAnswerメソッドとなります。
具体例
例えば、以下のコードを試験したい場合を想定します。
public class AnyClass {
public boolean anyMethod(Path pathname) {
// ...(ファイルに対する処理)
try {
// この処理の実施直前にpathnameで示すファイルが削除された場合の振る舞い(NoSuchFileExceptionが発生)を確認したい
FileTime fileTime = Files.getLastModifiedTime(pathname);
} catch (NoSuchFileException e) {
System.out.println("ファイルタイムスタンプ取得時にファイルが存在しない");
return false;
}
// ...(ファイルに対する後処理)
return true;
}
}
上記の例のように、ファイルタイムスタンプを取得する直前で当該ファイルを削除した上で、実際のメソッド(ここではFiles.getLastModifiedTime)の振る舞いを確認したいというケースがあったとします。
その場合、Files.getLastModifiedTimeが呼ばれたタイミングで当該ファイルを削除した上で実際のメソッドを呼び出したい訳ですが、そのようなケースに対応する場合は、以下のようなテストコードで解決できます。
// 対象となるファイル
File file = new File("c:\\tmp\test.txt");
// Filesの各メソッドをmock化(ただしMock未定義の場合は通常は実際のメソッドを呼び出す)
try (MockedStatic<Files> mocked = mockStatic(Files.class, Mockito.CALLS_REAL_METHODS)) {
// 上記ファイルパスに対するファイルタイムスタンプ取得が行われた場合
// thenAnswerを利用して、スタブ化したメソッド内で任意の処理を実行する
mocked.when(() -> Files.getLastModifiedTime(eq(file.toPath())))
.thenAnswer(invocation -> {
// 当該ファイルを削除する
file.delete();
// その後実際のメソッドを呼び出し、ファイル削除後の実際の振る舞いを確認する
return invocation.callRealMethod();
});
// メソッド実行時ファイルが存在しない場合はNosuchFileExceptionが発生しfalseが返ることを確認
assertFalse(new AnyClass.anyMethod(file.toPath());
}
この例の場合は、staticメソッドをMock化する必要があるためmockStaticを利用するといおう特殊な方法でMockを用いてますが、thenAnswerの使い方については、通常のmockも基本同じです。