Mockitoとは、Javaのユニットテストのために開発されたモックフレームワークです。
テストでモックオブジェクトを直感的に操作できるのを目的として開発されています。
Mockitoを知るには、周辺知識である「モックフレームワーク」、「モック」、「モックオブジェクト」などを知る必要があります。Mockitoを解説する前にこれらのワードについて説明します。
事前知識
モックフレームワーク
モックフレームワークとは、ユニットテストのために開発されたツールです。
テスト対象のクラスやメソッドが依存している部分を模擬的に置き換える為に使用されます。
これにより、テスト対象のコードが正しく動作するかどうかを独立して検証することが可能になります。
モックフレームワークは、以下のような場合に特に有用です。
- テスト対象のコードが外部リソース(データベース、ネットワーク、ファイルシステムなど)に依存している場合
- テスト対象のコードが他のクラスやメソッドに強く依存している場合
- テスト対象のコードがまだ完成していない部分に依存している場合
※モックフレームワークの例:Mockito、PowerMock など
Mock(モック)
要するにハリボテという意味です。
テスト対象のクラスが「まだ実装が完了していないクラス」を呼び出したときに、モックを使えば仮の値を返却させることができるようになります。
モックオブジェクト
テスト対象(テストしたいクラス)から呼び出される依存先のオブジェクトに代わって使用されるテスト用のオブジェクトのことです。
モック化
モック化とは、実際のオブジェクトを模擬した「モックオブジェクト」を作成し、それを使用することを指します。
(補足)
・ユニットテスト(単体テスト):プログラムを構成する最小単位となる「モジュール」の機能が正しく動作しているかを確認するテストのことです。
・モジュール:関数やメソッドなどのコードのまとまり。
Mockitoとは
JavaのモックフレームワークであるMockitoは、モックオブジェクトを直感的に操作できるよう設計されています。
Mockitoは、テスト中に本物のオブジェクトの代わりに使用されるモックオブジェクトを作成し、その動作を定義することができます。
具体的には、Mockitoでは以下のことが可能です
- モックオブジェクトの作成
- モックオブジェクトがどのように呼び出されるべきかの定義(どのメソッドが何回呼び出され、どのような引数で呼び出されるかなど)
- モックオブジェクトのメソッドが呼び出されたときの戻り値の設定
これにより、まだ完成していない部分や外部のシステムとのやり取りを模擬的に行うことができます。これは、例えば、まだ実装が完了していないクラスを呼び出す必要がある場合に、モックを使って仮の値を返すなどの操作を可能にします。つまり、Mockitoは「まだ完成していない部分」を仮想的に作り出すことで、テストを効率的に進めることができます。
Mockitoで出来ないこと
・privateメソッドのモック化
・staticメソッドのモック化
・protectedメソッドのモック化
・インスタンス生成(new)のモック化
・抽象クラスのモック化
→上記をモック化するには、Mockitoとは別でPowerMockを使用しなければいけません。
それ以外であれば基本的にmockitoでモック化することができます。
Mockitoの導入方法
Mavenを使用している場合、pom.xmlに以下を追記すればOKです。
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>4.0.0</version>
</dependency>
※versionは任意のものに書き換えて下さい。
Mockitoを使用した簡単なサンプルソース
以下のようなCalc.javaを作りました。これのテストコードを書きたい!でも、Calc.javaの中で呼んでるSubCalc.javaの実装はまだ終わっていない…。こんなときにMockitoを使います。
Mockitoを使えば、SubCalc.javaの実装がまだ終わっていなくてもCalc.javaのテストコードを書くことが可能です。
⇓⇓ Calc.javaのテストコード ⇓⇓
package products;
import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
public class CalcTest {
@InjectMocks
private Calc calc;
@Mock
private SubCalc subCalc;
@Before
public void initMocks() {
MockitoAnnotations.initMocks(this);
System.out.println("initMocksが呼ばれました。");
}
@Test
public void test01(){
// SubCalcクラスの「getValA」メソッドをモックし、実行された場合は3を返す。
Mockito.doReturn(3).when(subCalc).getValA();
// SubCalcクラスの「getValB」メソッドをモックし、実行された場合は4を返す。
Mockito.doReturn(4).when(subCalc).getValB();
// SubCalcクラスの「getValC」メソッドをモックし、実行された場合は5を返す。
Mockito.doReturn(5).when(subCalc).getValC(anyInt(), anyInt());
// 実行して結果を受け取る
int actual = calc.add();
// 期待値の設定
int expected = 12;
// 検証
assertEquals(expected,actual);
}
@Test
public void test02(){
// ここは空振りするけど、initMocksは実行される。
}
}
【テストコードの解説】
-
@InjectMocksアノテーション
テスト対象のクラスに対して、このアノテーションを付与します。
今回テストしたいクラスはCalc.javaなので、このクラスに付けています。
そうすることで@Mockアノテーション
を付けたクラスのインスタンスを自身に注入することができます。 -
@Mockアノテーション
「まだ実装が完了していないクラス」に対して、このアノテーションを付与します。
そうすることで、実装中の未完成のメソッドを呼び出しても仮の値を返却させるように処理を記述することができるようになります。
それをモック化といいます。 -
@BeforeアノテーションとMockitoAnnotations.initMocks(this)
今回、initMocks()に@Before
を付けています。
@Before
を付けたinitMocks()は、テストメソッドが実行される前に必ず処理が実行されるようになります。
つまり、各テストメソッドの実行前にMockitoAnnotations.initMocks(this)を実行しているということになります。
この処理が呼ばれたタイミングでモックインスタンスを注入しています。 -
doReturn
文法は以下のとおりです。
// 文法
Mockito.doReturn(戻り値).when(モックインスタンス).メソッド名(引数)
// 引数なし
Mockito.doReturn(3).when(subCalc).getValA();
// 引数あり
Mockito.doReturn(5).when(subCalc).getValC(anyInt(), anyInt());
- anyInt()
getValC(int x, int y)は、見ての通り引数がint型です。
テストコードとしては、Mockito.doReturn(5)の部分で戻り値として5が返却されるようにモックしているので、
getValC(int x, int y)にどんな数値を渡しても意味がないということになります。
こういう場面(どんな値を渡してもいいけど、型はint型になっている必要があるとき)は、anyInt()を使用します。
MockitoとJunitの違い(関連)
JUnitとMockitoは、Javaのユニットテストを行うためのフレームワークですが、それぞれ異なる目的と機能を持っています。
・JUnitは、ユニットテストの作成と実行を支援するフレームワークです。JUnitはテストケースを作成し、それらを実行して結果を報告します。JUnitはテストの基本的なフレームワークであり、テストケースの作成、実行、結果の検証などを行います。
・Mockitoは、モックフレームワークと呼ばれるものです。Mockitoは、テスト対象のクラスやメソッドが依存している部分を模擬的に置き換えるために使用されます。これにより、テスト対象のコードが正しく動作するかどうかを独立して検証することが可能になります。
JUnitはテストケースの作成と実行を行い、Mockitoはテスト対象の依存関係を模擬的に置き換えてテストを効率的に行うためのツールです。
これらは一緒に使われることが多く、JUnitでテストケースを作成し、Mockitoで依存関係をモック化してテストを行います。