先のテストコードの最後に、以下のように書かれていました。
// モックの検証(メソッドの戻り値で判断できているので冗長な確認)
AndroidMock.verify(mockHttpClient);
「verify()」は、モックにあらかじめ定義したメソッドがすべて呼ばれたことを検証し、定義と異なる使われ方をしていた場合に例外を発生してテストを失敗させます。
コメントに「冗長な確認」と書いていますが、これは筆者が「あまりverify()に頼らないように」するための戒めです。テスト対象メソッドの実装にまで踏み込んだ検証を乱用すると変更に弱いテストコードになってしまうので、verify()による検証は補助手段に留めるべきでしょう。
verify()の検証内容は、モックの生成時点で以下の3パターン存在します。
いずれもverify()では、expect()されたにもかかわらず呼び出されていないメソッドが残っていると例外を発生してテストを失敗させます。StrictMockの利用例をHTTP通信の正常系テストケースから抜粋して紹介します。
// 上で定義したモックオブジェクトを返すHttpResponseモック
// ステータスを確認してからEntityを取得していること(順序)を確認するため、StrictMock.
HttpResponse mockResponse = AndroidMock.createStrictMock(HttpResponse.class);
AndroidMock.expect(mockResponse.getStatusLine()).andReturn(mockStatusLine);
AndroidMock.expect(mockResponse.getEntity()).andReturn(mockHttpEntity);
AndroidMock.replay(mockResponse);
このように定義していると、getStatusLine()を呼び出さずに(つまり、HTTPステータスを確認せずに)getEntity()で本文を取得しているコードを検出できます。
記事中ではモックオブジェクトと表現していますが、モックの定義や分類には、さまざまな流儀があります。
著名なユニットテストの実装パターン集である『xUnit Test Patterns(および同名の書籍)』では、これを「Test Double」と総称したうえで、テスト対象への間接入力を操作する「Test Stub」、テスト対象からの間接出力を受け取り記録する「Test Spy」、また間接出力の検証までオブジェクト内で行う「Mock Object」などに分類しています。
興味を持たれた方は、ぜひ原書や読書会のログなどを読んでみてください。
今回はHTTP通信という外部要因のテストのためにモックを使用する方法を紹介しましたが、モックオブジェクトは「テスト対象が依存するコンポーネントをモックに置き換えてテストする」テクニックであり、テスト対象をクリーンな環境で確実にテストする目的で広く適用できます。
HttpClientに限らず、DateやPreferencesなど、モックオブジェクトを導入することでユニットテストが可能になる範囲は広がるはずです。これは次回紹介するCIの導入において前提ともいえるので、ぜひ導入を検討してみてください。
ただし、過度にモックを多用、依存することはテストコードの保守性を低下させます。適用すべき個所を見極めて、良いテストコードを書きましょう。
なお、本稿で掲載したソースコードは以下のリポジトリで公開されています。
長谷川 孝二(@nowsprinting)
元メーカー系、現在フリーランス(個人企業法人)のエンジニア。これまでサーバサイドから携帯電話まで大小のJava案件にかかわり、現在はiOS/Androidアプリの請負開発が主軸。
自作アプリ『山吹色の茸疾走』のロジック強化が最近の息抜き
Copyright © ITmedia, Inc. All Rights Reserved.