2014.04.10

Mockito

ユニットテストを書くときに、HttpClient など通信をするモジュールを使ってると、
実際に通信が始まっちゃうと困ることが多々ある。

その通信部分をモックに置き換えて、テスト用の出力を返してもらうだけにする。
JUnit を使っているのだが、どうやってモックに置き換えるかが問題。

実際に使ったのは PowerMockito だけど、まずはその元となっている Mockito から紹介。
https://code.google.com/p/mockito/


モックを生成するのは、単純に Mockito.mock を呼べばいい。
指定したクラスのインスタンスを作ってくれるので、テスト対象のクラスに投げるのに使える。

例えば、HttpResponse のインスタンスが欲しいなーと思っても、
new して作ると、引数が必要だったり、その引数を用意するために別のインスタンスが必要だったりする。
それが1行書くだけでいいので、実際のインスタンス生成のメンドクサさはなくなる。

        HttpResponse httpResponseMock = Mockito.mock(HttpResponse.class);

ただ、モックもちゃんと動作をしてもらわないとテストに使えないので、必要なメソッドだけ動作を定義する。
例えば、そのモックの getStatusLine() を呼んだら statusLineMock を返すようにする。
その statusLineMock もモックで作っておき、getStatusCode() が呼ばれたら SC_OK を返す、など。

    StatusLine statusLineMock = Mockito.mock(StatusLine.class);
    Mockito.when(statusLineMock.getStatusCode()).thenReturn(HttpStatus.SC_OK);
    Mockito.when(httpResponseMock.getStatusLine()).thenReturn(statusLineMock);

この Mockito.mock だとインスタンスを丸ごとがモックになる。
本物のインスタンスの動きをして欲しい部分があるときには逆にメンドクサイ。

そういうときに使うのが、Mockito.spy というもの。
テスト対象のクラスはモックにするわけにはいかず、実際の動作をしてくれないとテストの意味が無い。
そこで、どうしてもモックにしたいメソッドだけを指定する。

・Mockito.mock は全体をモックにしてからメソッドを置き換えて動作を定義していく
・Mockito.spy は本物をベースにしてメソッドを一部モックにする

例えば、TestTarget というクラスがあるとする。
getData() が内部で HttpClient を使って通信して、その結果を返すとする。

ここで HttpClient をモックにしたいので、getHttpClient() というメソッドを作り、その動作を置き換える。

public Class TestTarget {
    public TestTarget(String arg1, String arg2) {
    }

    public String getData() {
        HttpClient httpClient = getHttpClient();
        HttpResponse response = httpClient.execute(...
        String result = ...
        return result;
    }
    
    public HttpClient getHttpClient() {
        return HttpClients.createDefault();
    }
}

1. Mockito.spy にテスト対象のクラスを渡して、メソッドを差し替える準備をする。

    TestTarget instance = Mockito.spy(new TestTarget("TEST_VALUE1","TEST_VALUE2"));

2. when と thenReturn で何をしたら何を返すかを書く。
getHttpClient() を呼んだら、モックである httpClientMock を返すようにする。
Mockito だと差し替え対象のメソッド getHttpClient() は public じゃないといけないという制約がある。

    HttpClient httpClientMock = Mockito.mock(HttpClient.class);
    Mockito.when(instance.getHttpClient()).thenReturn(httpClientMock);

3. 置き換え後のインスタンスでテストをする。

    Assert.assertNotNull(instance.getData());

4. HttpClient の代わりに HttpClientMock が呼ばれる。

こんな感じで使う。

ポイントは 2. の箇所で、public メソッドじゃないとダメなところ。
この部分を private メソッドでもOKにしたのが、PowerMockito なのである。

つづく


2014.04.10, 22:54 / PC関係