Cartクラスでのモック使用例
Cartクラスは、ショッピングサイトなどでお客様のカート情報を管理するクラスです。
このクラスのadd(Shouhin shouhin, int num)メソッドは、引数で渡された商品をカートに入れるクラスになります。引数で渡しているShouhinクラスは、Hibernateのモデルクラスになります。まずは、以下のソースをざっと読んでみてください。
public class Cart { public int add(Shouhin shouhin, int num) { ・ ・ ・ if (!canBuy(shouhin)) { return CANNOT_BUY; } else { return ADD_OK; } ・ ・ ・ } private boolean canBuy(Shouhin shouhin) { int status = shouhin.inquireStatus(); // DB問い合わせ if (status == Shouhin.STATUS_SOLD_TO_END || // 終売 status == Shouhin.STATUS_SOLD_OUT) { // 売切 return false; } else if (status == Shouhin.LIMITED_NUM) { // 数量限定 ・ ・ ・ // 買える個数であればtrue ・ ・ } else { return true; // 購入可能 } } }
このメソッドの18行目にあるshouhin.inquireStatus()では、商品のステータスをデータベースに問い合わせます。単純にフィールド値を返すのではなく、DBアクセスをしてステータスを取得するメソッドです。Cartクラスのadd(Shouhin shouhin, int num)メソッドのユニットテストを書くのであれば、次の4パターンの商品状態をデータベースに作る必要があります。
- 終売
- 売切
- 数量限定
- 購入可能
ユニットテストとしては、CartクラスのテストなのでShouhinクラスのDBアクセスまで検証する必要はありませんが、テストには必要なのでデータベースにその状態を作らなければなりません。データベースのレコード作成は、非常にシンプルなテーブルであれば、さほど手間も時間も掛からないでしょう。しかし、商品のような付随情報が多いテーブルとなると、関わるテーブルも多くなり、想像しているよりも沢山のデータを作る必要があります。このような場合に、モックが役に立つのです。以下がmockitoを使って書いたテストケースです。
// 終売 Shouhin shouhinMock = mock(Shouhin.class); when(shouhinMock.inquireStatus()).thenReturn(Shouhin.STATUS_SOLD_TO_END); assertThat(cart.add(shouhinMock, 1), is(Cart.CANNOT_BUY)); // 売切 shouhinMock = mock(Shouhin.class); when(shouhinMock.inquireStatus()).thenReturn(Shouhin.STATUS_SOLD_OUT); assertThat(cart.add(shouhinMock, 1), is(Cart.CANNOT_BUY)); // 数量限定 shouhinMock = mock(Shouhin.class); when(shouhinMock.inquireStatus()).thenReturn(Shouhin.STATUS_SOLD_OUT); assertThat(cart.add(shouhinMock, 1), is(Cart.ADD_OK)); // 購入可能 shouhinMock = mock(Shouhin.class); when(shouhinMock.inquireStatus()).thenReturn(Shouhin.CAN_BUY); assertThat(cart.add(shouhinMock, 1), is(Cart.ADD_OK));
「// 終売」のテストコードを例に、コードを説明します。
まず1行目の以下文でShouhinクラスのモックを作っています。
Shouhin shouhinMock = mock(Shouhin.class);
この1行だけで、Shouhinクラスのモックを簡単に作ることができます。
mock()メソッドはMockitoクラスのstaticメソッドではありますが、static importをしているため、このような書き方が可能です。
続いて2行目です。こちらではinquireStatus()メソッドをモック化しています。
when(shouhinMock.inquireStatus()).thenReturn(Shouhin.STATUS_SOLD_TO_END);
when()メソッドもMockitoクラスのstaticメソッドで、static importをしているため、このような書き方ができます。
when(①xxxxxxxxxx).thenReturn(②xxxxxxxxxx);
この文を見て、何か気づくことはないでしょうか?
JUnitはテストケースを自然言語(普通の英文)のように読めるように工夫されています。mockitoも同じ思想のようで、「①xxxxxxxxxxの場合(時)は、②xxxxxxxxxxをリターンします」と訳せると思います。直感的に読むことができて理解しやすいです。
このことが分かっていれば、プログラムは容易に理解できるのではないでしょうか。引数には、戻り値を指定したいmethodCall: shouhinMock.inquireStatus()を指定します。そしてthenReturnには、メソッドの戻り値: Shouhin.STATUS_SOLD_TO_ENDを指定します。これでメソッドをモック化することができました。
このようにモック化したinquireStatus()を呼ぶと、Shouhin.STATUS_SOLD_TO_ENDが返るようになります。3行目はassertThat()でcart.add()の検証をしています。こちらについては、基本的なJUnitの知識があることを前提としていますので、本稿での詳しい説明は割愛します。
assertThat(cart.add(shouhinMock, 1), is(Cart.CANNOT_BUY));
例では、実際にmockitoを使用しているテストコードの説明をしました。mockitoを使い、Shouhinクラスのモックを作ることにより、このように複数のパターンのテストコードを簡単に記述できます。このやり方であれば、データベースに状態を作る必要もないので、手間と時間が掛からず効率も良いと思います。
もちろんShouhinクラスのユニットテストで、データベースに状態を作ってのテストをする必要はあります。