web-dev-qa-db-ja.com

同じURIへの複数のリクエストを処理するSpring MockRestServiceServer(自動検出)

RESTサービスAのSpring統合テストを書いているとしましょう。このサービスは、別のRESTサービスBにヒットし、ヒットするURIのリストを取得しますRESTサービスC。これは一種の自動検出パターンです。MockRestServiceServerを使用してBおよびC応答をモックしたいです。
今、Bからの応答はURIのリストであり、それらはすべて非常に似ています。例のために、Bからの私の応答は次のようであるとしましょう。

{
    uris: ["/stuff/1.json", "/stuff/2.json", "/stuff/39.json", "/stuff/47.json"]
}

単にサービスAがサービスCのベースURLにそれぞれを追加し、それらの要求を行います。
Bのモックはリクエストが1つしかないため簡単です。
Cのモックは、すべてのURIを適切なモック応答にモックする必要があるため、面倒です。自動化したい!
最初に完全なURLではなく、その一部に一致する独自のマッチャーを作成します。

public class RequestContainsUriMatcher implements RequestMatcher {
    private final String uri;

    public RequestContainsUriMatcher(String uri){
        this.uri = uri;
    }

    @Override
    public void match(ClientHttpRequest clientHttpRequest) throws IOException, AssertionError {
        assertTrue(clientHttpRequest.getURI().contains(uri));
    }
}

これは今私がこれを行うことができるのでうまくいきます:

public RequestMatcher requestContainsUri(String uri) {
    return new RequestContainsUriMatcher(uri);
}

MockRestServiceServer.createServer(restTemplate)
            .expect(requestContainsUri("/stuff"))
            .andExpect(method(HttpMethod.GET))
            .andRespond(/* I will get to response creator */);

必要なのは、完全なリクエストURLと、モックデータの場所を知っている応答作成者だけです(テストリソースフォルダーにjsonファイルとして保存します)。

public class AutoDiscoveryCannedDataResponseCreator implements ResponseCreator {
    private final Function<String, String> cannedDataBuilder;

    public AutoDiscoveryCannedDataResponseCreator(Function<String, String> cannedDataBuilder) {
        this.cannedDataBuilder = cannedDataBuilder;
    }

    @Override
    public ClientHttpResponse createResponse(ClientHttpRequest clientHttpRequest) throws IOException {
        return withSuccess(cannedDataBuilder.apply(requestUri), MediaType.APPLICATION_JSON)
                    .createResponse(clientHttpRequest);
    }
}

物事は簡単です。リクエストURIを文字列として受け取り、モックデータを文字列として返すビルダーを作成する必要があります。鮮やかさ!

public ResponseCreator withAutoDetectedCannedData() {
    Function<String, String> cannedDataBuilder = new Function<String, String>() {
        @Override
        public String apply(String requestUri) {
            //logic to get the canned data based on URI
            return cannedData;
        }
    };

    return new AutoDiscoveryCannedDataResponseCreator(cannedDataBuilder);
}

MockRestServiceServer.createServer(restTemplate)
            .expect(requestContainsUri("/stuff"))
            .andExpect(method(HttpMethod.GET))
            .andRespond(withAutoDetectedCannedData());

それはうまくいきます! ....最初のリクエストの場合。
最初のリクエスト(/stuff/1.json)の後、MockRestServiceServerは「アサーションエラー:これ以上のリクエストはありません」というメッセージで応答します。
基本的に、そのMockRestServiceServerに対して.expect()呼び出しがあったのと同じ数の要求を行うことができます。そして、そのうちの1つしかなかったので、最初のリクエストだけが通過します。
それを回避する方法はありますか?サービスCを10回または20回モックしたくありません...

16

MockRestServiceServerクラスを見ると、2つの 'expect()'メソッドがサポートされています。最初のデフォルトは「ExpectedCount.once()」ですが、2番目の方法ではこの値を変更できます

public ResponseActions expect(RequestMatcher matcher) {
    return this.expect(ExpectedCount.once(), matcher);
}

public ResponseActions expect(ExpectedCount count, RequestMatcher matcher) {
    return this.expectationManager.expectRequest(count, matcher);
}

私はこのチケットを見つけました MockRestServiceServerは複数回の期待が可能になるはずです これは2番目の方法のいくつかのオプションの概要を示しています。

あなたの場合、私は静的インポートを追加し、manyTimes()メソッドを使用することはforループよりもきれいなコードだと思います

MockRestServiceServer
            .expect(manyTimes(), requestContainsUri("/stuff"))
            .andExpect(method(HttpMethod.GET))

他のオプションは

once();
manyTimes();
times(5);
min(2);
max(8);
between(3,6);
23
emeraldjava

編集:@emeraldjavaからの回答を参照してください。これは、Spring 4.3+ユーザー向けの正しいソリューションを示しています。

残念ながら、複数の呼び出しを予期するNiceメカニズムはありません。手動で行うか、ループを使用します。例:

for (int i = 0; i < 10; i++) {           
        mockRestServiceServer
                .expect(requestContainsUri("/stuff"))
                .andExpect(method(HttpMethod.GET))
                .andRespond(withAutoDetectedCannedData());
}

リクエストは中断することなく呼び出す必要があることに注意してください。 「/ stuff」URIと一致しない別のREST呼び出しは存在できません。

10
rapasoft