モックとは何か
テスト対象クラスで使用している外部オブジェクトの代わりに模擬の外部オブジェクトを作成することです。
モックを利用することで外部オブジェクトの動作に影響されずにテスト対象クラスの動作をテストすることができます。
※モックフレームワークによって違いがあるため、この記事ではMockitoを例として説明します。
以下のコードでは、ExampleServiceImpl
クラスの method01
メソッドが ExampleRepository
からデータを取得しています。この ExampleRepository
をモックに置き換えることでExampleServiceImpl
クラスの method01
メソッドが正しく動作するかを、ExampleRepository
の実装に依存せずにテストすることができます。
サービスのサンプルコード
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class ExampleServiceImpl {
@Autowired
private ExampleRepository exampleRepository;
public String method01() {
// データ取得のためのInputBeanの設定
ExampleRepository dataInputBean = new ExampleRepository();
dataInputBean.setName("A");
// リポジトリからデータを取得
return exampleRepository.getData(dataInputBean);
}
}
なぜモックを使うのか
テストの独立性
モックを使うことで、テスト対象クラスのコードを外部オブジェクトの影響を受けずにテストすることができます。
これにより、テストの信頼性と保守性を高めることができ、外部オブジェクトの実装が未完成であっても対象クラスのテストを実施できます。
テストの柔軟性
モックは、テスト対象のコードに対して様々な入力値や条件を設定することができます。これにより、網羅的なテストが可能になり、境界値テストや異常系のテストを簡単に実施できます。
テストの実行速度
モックを使うことで、実際の外部オブジェクトとやり取りする必要がないため、テストの実行速度を上げることができます。
サンプルテストコード
// テスト対象クラス
public class ExampleService {
// ExampleRepositoryをインジェクション
@Autowired
private ExampleRepository exampleRepository;
// ユーザーIDを取得する
public User getUser(int id) {
return exampleRepository.findById(id);
}
}
// テストコード
@Test
public void testGetUser_Success() {
// モックの作成
ExampleRepository exampleRepositoryMock = Mockito.mock(ExampleRepository.class);
// モックの挙動を設定(正常系)
// exampleRepositoryのfindById()メソッドが呼び出されたら、引数の値に関係なく、Userオブジェクトを戻り値として設定する
Mockito.when(exampleRepositoryMock .findById(any())).thenReturn(new User(1, "John Doe"));
// テスト対象のコードにモックを注入
ExampleService exampleService = new ExampleService(exampleRepositoryMock);
// テストの実行
User user = ExampleService.getUser(1);
// テスト結果の検証
assertEquals(1, user.getId());
assertEquals("John Doe", user.getName());
}
モックはどう動くか
1. モックの作成
モックフレームワークのAPIを使用して、モックオブジェクトを作成します。
モックを作成する対象のインターフェースまたはクラスを指定します。これにより、モックオブジェクトがどのメソッドを持っているかが決まります。
// モックの作成
ExampleRepository exampleRepositoryMock = Mockito.mock(ExampleRepository.class);
2. モックの設定
when()メソッドを使用してモックオブジェクトのメソッドに対して、期待される挙動を設定します。
// モックの挙動を設定(正常系)
Mockito.when(exampleRepositoryMock.findById(any())).thenReturn(new User(1, "John Doe"));
呼び出される際に渡される引数、戻り値、メソッドが戻す例外を設定することができます。
上記のコードでは、引数にかかわらず、exampleRepositoryのfindById()メソッドが呼び出されたら、「IDが1,名前がJohn Doe」であるUserオブジェクトを戻り値として返します。
3. テスト対象のコードへの注入
テスト対象のコードのコンストラクタにモックオブジェクトを渡します。これにより、テスト対象のコードはモックオブジェクトを依存関係として利用することができます。
コンストラクタにモックオブジェクトを渡す方法はコンストラクタインジェクションと呼ばれ、他にはフィールドにモックオブジェクトを渡すフィールドインジェクションもあります、
// テスト対象のコードにモックを注入
ExampleService exampleService = new ExampleService(exampleRepositoryMock);
// フィールドインジェクション
@Mock
ExampleRepository exampleRepositoryMock
4. テストの実行
テスト対象のコードのメソッドを呼び出し、実際の動作を実行します。
// テストの実行
User user = ExampleService.getUser(1);
5. 検証
テスト対象のコードの実際の動作が、期待される動作と一致していることを検証します。
// テスト結果の検証
assertEquals(1, user.getId());
assertEquals("John Doe", user.getName());
また、検証には他にもモックメソッドの呼び出し引数が正しいことを検証するArgumentCaptureやメソッドが呼び出された回数を検証するverifyメソッドなどがあります。