ヒトリ歩き

愚痴とかいろいろ書きます

MockRestServiceServerで使ってRestTemplateをモックする

はじめに

RestTemplateを使用しているメソッドをテストする際に、RestTemplateの処理をモックにしないといけない。
その場合に、Mockito以外のやり方がないか調べてみたところ、MockRestServiceServerというクラスをSpring Testが提供していることが分かった。
簡単なプログラムを使ってMockRestServiceServerを使ってみる。
MockRestServiceServerとは何かを説明すべきだろうが、他の方がまとめてくださっているので、参照してもらえればと思う。

qiita.com

Getリクエストのモック

テスト対象のソースは以下のとおり。

package jp.sample.resttemplate.repository;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import jp.sample.resttemplate.model.Products;

@Component
public class RepositoryImpl implements Repository {

    private Logger logger = LoggerFactory.getLogger(RepositoryImpl.class);

    private RestTemplate restTemplate;

    public RepositoryImpl(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @Override
    public Products getProducts() {
        Products products = this.restTemplate.getForObject("http://localhost/products", Products.class);
        logger.info(products.toString());
        return products;
    }
}

まずは、MockRestServiceServerクラスの簡単な使い方を説明する。

MockRestServiceServerクラスのbindToメソッドのパラメータに実行する際に使用するRestTemplateクラスのオブジェクトを指定し、buildメソッドでMockRestServiceServerクラスのオブジェクトを生成する。

MockRestServiceServer mockServer = MockRestServiceServer.bindTo(this.restTemplate).build();

MockRestRequestMatchers#requestToメソッドのパラメータにURLを指定する。
andRespondメソッドのパラメータに応答結果のモックデータを指定する。

mockServer.expect(requestTo("http://localhost/products"))
            .andRespond(withSuccess("{\"products\":[ { \"productCode\": 1234, \"supplierCode\":4566, \"productName\":\"test\", \"productPrice\": 2000, \"maker\":\"happy happy\"}]}", MediaType.APPLICATION_JSON));

上記の内容を踏まえてテストソースは以下のようになる。

void testGetProducts1() {

    MockRestServiceServer mockServer = MockRestServiceServer.bindTo(this.restTemplate).build();

    mockServer.expect(requestTo("http://localhost/products"))
                .andRespond(withSuccess("{\"products\":[ { \"productCode\": 1234, \"supplierCode\":4566, \"productName\":\"test\", \"productPrice\": 2000, \"maker\":\"happy happy\"}]}", MediaType.APPLICATION_JSON));

    Products actual = repo.getProducts();

    assertAll(
            () -> {
                assertEquals(1, actual.getLen());
            },
            () -> {
                Product product = actual.getProduct(0);
                assertAll(
                        () -> assertEquals(1234, product.getProductCode()),
                        () -> assertEquals(4566, product.getSupplierCode()),
                        () -> assertEquals("test", product.getName()),
                        () -> assertEquals(2000, product.getPrice()),
                        () -> assertEquals("happy happy", product.getMaker())
                 );
            }
    );
}

応答結果のモックはString型で指定するか、Resource型を指定するか

String型で応答結果を作成すると、データ量が多いデータであれば応答結果を作成するのが大変だと思う。
応答データを定義したJSONファイルを用意することで応答データの作成が楽になる。
テスト単位で応答データを用意することにはないってしまうが、JSONファイルを用意する方がテストの修正コストは少なくて済むと思う。
JSONファイルを使用する場合は、ClassPathResourceクラスを使用し、クラスパスが通っているディレクトリからファイルをロードし、andRespondメソッドのパラメータに指定する。

Resource responseBody = new ClassPathResource("ResponsePattern1.json");
mockServer.expect(requestTo("http://localhost/products"))
            .andRespond(withSuccess(responseBody, MediaType.APPLICATION_JSON));

テストソースと同一ディレクトリでJSONファイルを管理するのであれば、以下のようにClassPathReosourceのコンストラクタの第2パラメータにthis.getClass()の戻り値を指定する。

// テストソースと同一ディレクトリのJSONファイル
Resource responseBody = new ClassPathResource("ResponsePattern2.json", this.getClass());
mockServer.expect(requestTo("http://localhost/products"))
            .andRespond(withSuccess(responseBody, MediaType.APPLICATION_JSON));

最後に

Mockitoを使わずにRestTemplateがモックできることが分かった。
Mockitoを使用せずに、細かいヘッダなども確認することができるため、RestTemplateのモックをする場合はMockRestServiceServerを使用する方がよいと思う。
また、応答データはString型ではなくJSONファイルで管理することで応答データの作成も効率よくできると思うので、JSONファイルを用意するようにしたいと思う。