Mockitoは、JavaおよびKotlinのユニットテストでモックオブジェクトを作成するためのオープンソースフレームワークであり、テスト対象コードを外部依存関係から分離することができます。これにより、開発者は実際のリポジトリ、APIクライアント、データベースを、事前定義された動作を持つ制御可能なスタブに置き換えます。Mockito.orgによると、このライブラリはユニットテストを使用するJavaプロジェクトの60%以上で使用されています。
重要なポイント
Mockitoは、Java、Kotlin、その他のJVM言語向けのユニットテストでモックオブジェクト(スタブ)を作成するためのオープンソースライブラリです。テストの実行を担当するJUnitとは異なり、Mockitoは分離の問題を解決します — テスト対象クラスの実際の依存関係を予測可能なオブジェクトに置き換えます。
モックなしでデータベースや外部APIにアクセスするメソッドをテストするには、実際の環境のセットアップ — データベースのデプロイ、サーバーの起動 — が必要です。Mockitoはこれらの依存関係を固定の動作を持つオブジェクトに置き換えます。repository.findById(1)メソッドは、データベースにアクセスせずに常に指定されたUserオブジェクトを返します。
MockitoのアーキテクチャはProxyパターン(インターフェースとクラス向け)に基づいています。ライブラリは指定された型のサブクラスまたはプロキシを生成し、すべてのメソッド呼び出しをインターセプトして、デフォルト値またはwhen().thenReturn()で設定された値を返します。
Mockitoの動作原理は、モックの作成、動作の設定(スタビング)、呼び出しの検証(ベリフィケーション)の3つの基本操作に基づいています。各操作はorg.mockito.Mockitoクラスの静的メソッドを使用します — Maven Centralの統計によると、Javaエコシステムで最もダウンロードされているクラスです。すべてのモックメソッド呼び出しはメモリに記録され、後でverifyを使用して検証できます。
Mockitoを使った典型的なテストは3つのフェーズで構成されます: Arrange — when().thenReturn()を使用したモックの作成とスタブの設定、Act — テスト対象メソッドの呼び出し、Assert — assertEqualsとverify(mock)を使用した結果の確認。このアプローチはAAA(Arrange-Act-Assert)と呼ばれます。
Mockitoがユーザーリポジトリを置き換える簡単なテストを見てみましょう。when().thenReturn()メソッドは、findById呼び出しが事前に準備されたUserオブジェクトを返すようにモックを設定します。
// リポジトリモックを作成
UserRepository mockRepo = mock(UserRepository.class);
// 動作を設定: findById(1)がユーザーを返す
when(mockRepo.findById(1)).thenReturn(new User("Alice"));
// メソッドが実際に呼び出されたことを確認
User result = mockRepo.findById(1);
assertEquals("Alice", result.getName());
verify(mockRepo).findById(1);
Mockitoはモックを作成する2つの方法を提供します: 静的メソッドmock(Class)と、MockitoAnnotations.openMocks()を使用した初期化を伴う@Mockアノテーションです。最初の方法は1〜2個のモックに適しており、2番目の方法は依存関係が多い場合に便利です — アノテーションによってボイラープレートコードが削減されます。
mock()メソッドはクラスを受け取り、when().thenReturn()で設定可能なスタブオブジェクトを返します。設定されていないすべてのメソッドはデフォルト値(数値は0、ブーリアンはfalse、オブジェクトはnull)を返します。
ApiClient apiClient = mock(ApiClient.class);
Database database = mock(Database.class);
@Mockアノテーションと@ExtendWith(MockitoExtension.class)を組み合わせると、テストクラスのすべてのフィールドに対してモックが自動的に作成されます。MockitoExtensionは各テストの前の初期化を担当します。
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Test
void getUserShouldReturnUserFromRepo() {
when(userRepository.findById(1)).thenReturn(new User("Alice"));
User result = userService.getUser(1);
assertEquals("Alice", result.getName());
}
}
Stubbing(スタビング)とは、特定の引数で呼び出されたときにモックメソッドが何を返すべきかを定義するプロセスです。基本構文: when(mock.method(args)).thenReturn(value)。さまざまなシナリオに対応するため、Mockitoは複数のthenメソッドのバリエーションを提供しています。
| メソッド | 目的 |
|---|---|
| thenReturn(value) | 常に指定された値を返す |
| thenThrow(exception) | 呼び出し時に例外をスローする |
| thenAnswer(answer) | 戻り値を動的に計算する |
| thenCallRealMethod() | 実際のメソッドを呼び出す(部分モック) |
戻り値が呼び出し引数に依存する場合は、ラムダ式とともにthenAnswerを使用します。これは実際のデータを使った作業をシミュレートするのに便利です — 例えば、渡されたオブジェクトに基づいてIDを生成する場合などです。
when(repository.save(any())).thenAnswer(invocation -> {
User user = invocation.getArgument(0);
user.setId(42);
return user;
});
Verifyは、より古いモックオブジェクトライブラリ(EasyMock、jMock)にはないMockitoのユニークな機能です。
Verifyは、戻り値だけでなく副作用 — 結果を返さないメソッド(voidメソッド)の呼び出し — もチェックするため、テストの信頼性を高めます。verify(mock).methodName(args)メソッドは、特定のモックメソッドが指定された引数で呼び出されたかどうかを確認します。これにより、結果だけでなくプロセス — 依存関係へのアクセスの事実 — もテストできます。
デフォルトでは、verifyはメソッドが正確に1回呼び出されたことを確認します。異なる回数が必要な場合は、times(n)、atLeast(n)、never()、およびMockitoクラスのその他の修飾子を使用します。
// 呼び出し回数の確認
verify(repository, times(3)).save(any());
verify(repository, never()).delete(any());
verify(repository, atLeastOnce()).findById(1);
// 呼び出し順序の確認
InOrder inOrder = inOrder(repository);
inOrder.verify(repository).save(any());
inOrder.verify(repository).flush();
メソッドがどのオブジェクトで呼び出されたかを正確に確認する必要がある場合は、ArgumentCaptorを使用します。これは呼び出し中に引数の値をキャプチャし、そのフィールドを個別に確認できます。ArgumentCaptorは、テスト対象コードが内部でオブジェクトを作成し、それを依存関係に渡す場合に特に便利です — そうでなければそのオブジェクトを確認できません。
ArgumentCaptor<User> captor = ArgumentCaptor.forClass(User.class);
verify(repository).save(captor.capture());
assertEquals("Alice", captor.getValue().getName());
@Mockと@InjectMocksは、ボイラープレートコードを大幅に削減する2つの主要なMockitoアノテーションです。@Mockはフィールドのモックを作成し、@InjectMocksはテストクラスからすべてのモックを、コンストラクター、セッター、またはフィールドを介してテスト対象オブジェクトに注入します。
@InjectMocksのメカニズムは、次の順序で依存関係の注入を試みます: 最も多くの引数を持つコンストラクター、型によるセッター、プライベートフィールド。これらのいずれも機能しない場合、オブジェクトはnullの依存関係のままになり、テストはNullPointerExceptionで失敗します。
重要なのは:@InjectMocksはフィールドの型を分析しません — 型互換性のある任意のモックを代入します。クラスに同じ型のフィールドが2つある場合、Mockitoは誤ったモックを注入する可能性があります。そのような場合は、モックパラメーターを持つ明示的なコンストラクターを使用することをお勧めします。
Android開発では、MockitoはJUnitと一緒にViewModel、Repository、UseCaseのテストに使用されます。これらのクラスはAndroidコンテキストなしでJVM上で実行されるため、Mockitoはそれらの依存関係 — Room DAO、Retrofit API、SharedPreferences — を予測可能な動作のスタブに置き換えます。
AndroidプロジェクトにMockitoを追加するには、mockito-coreまたはmockito-inlineの依存関係を追加するだけです(後者はfinalクラスと静的メソッドのモック化をサポートしています)。バージョン5.12.0(2024年)にはJava 21のサポートと改善されたJUnit 5統合が含まれています。
// build.gradle.kts (module)
dependencies {
testImplementation("org.mockito:mockito-core:5.12.0")
testImplementation("org.mockito:mockito-junit-jupiter:5.12.0")
}
以前は、静的メソッドとコンストラクターのモック化にはPowerMock — バイトコードインストゥルメンテーションを通じて動作する拡張機能 — が必要でした。Mockito 5.x以降のmockito-inlineでは、この機能が直接組み込まれています: mockStatic(ClassName.class)を使用すると、追加のライブラリーなしで静的メソッドをモック化できます。
典型的なシナリオ: ViewModelがリポジトリメソッドを呼び出し、結果をUI状態に変換します。Mockitoがリポジトリを置き換え、テストはViewModelが成功応答とエラーの両方を正しく処理することを確認します。Clean Architectureを使用する場合、各レイヤー(DataSource、Repository、UseCase)に対してモックが作成されます — これにより各レイヤーを個別にテストできます。
よくある質問
Mockitoはプロキシとリフレクションを使用するJavaおよびKotlin向けのライブラリです。MockKはコルーチン、拡張関数、finalクラスを追加設定なしでサポートするKotlinファーストのライブラリです。
Mockito 2.1以降、finalクラスのモック化はオプトインでサポートされています。バージョン5.x(mockito-inline)では、これはデフォルトで有効です。mockito-inlineの依存関係を追加し、標準のmock()メソッドを使用するだけです。
Spyは部分的なモックで、デフォルトでは実際のメソッドを呼び出しますが、when().thenReturn()を使用していくつかのメソッドをオーバーライドできます。Spyは、クラス全体を書き換えられないレガシーコードのテストに便利です。
thenReturnは引数に関係なく常に同じ値を返します。thenAnswerは呼び出し — 引数、モック自体、状態 — に基づいて戻り値を計算します。動的な応答には常にthenAnswerを使用してください。
Verifyは結果だけでなく、プロセス — 依存関係へのアクセスの事実 — もチェックします。これはデータを保存したり通知を送信したりする必要があるサービスにとって重要です。verifyがないと、テストはメソッドがsave()やsend()を呼び出さなかったことを検出できません。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。