Spring MockRestServiceServer处理对同一URI的多个请求(自动发现) [英] Spring MockRestServiceServer handling multiple requests to the same URI (auto-discovery)

查看:179
本文介绍了Spring MockRestServiceServer处理对同一URI的多个请求(自动发现)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我正在为REST服务A编写Spring集成测试.该服务反过来又命中了另一个REST服务B,并获得了要在REST服务C上命中的URI列表.这是一种自动发现模式.我想使用MockRestServiceServer模拟B和C响应.
现在,来自B的响应是一个URI列表,它们都非常相似,并且为了示例起见,我来自B的响应是这样的:

Let's say I am writing Spring integration tests for a REST service A. This service in turn hits another REST service B and gets a list of URIs to hit on REST service C. It is kind of auto-discovery pattern. I want to mock B and C responses using MockRestServiceServer.
Now the response from B is a list of URIs, they are all very similar, and for the sake of the example lets say my response from B is like so:

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

仅服务A会将它们中的每一个附加到服务C的基本URL上,然后发出这些请求.
模拟B很容易,因为它只有1个请求.
模拟C很麻烦,因为我必须模拟每个URI才能获得适当的模拟响应.我想自动化!
因此,首先,我编写自己的匹配器以匹配完整的URL,而不是完整的URL:

Simply service A will append each of them onto base URL for service C and make those requests.
Mocking B is easy since it is only 1 request.
Mocking C is a hassle as I would have to mock every single URI to appropriate mock response. I want to automate it!
So first I write my own matcher to match not a full URL, but part of it:

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));
    }
}

这很好,因为现在我可以这样做:

This works fine as now I can do this:

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文件放在测试资源文件夹中):

Now all I need is a response creator that knows the full request URL and where the mock data sits (I will have it as json files in test resources folder):

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作为字符串并以字符串形式返回模拟数据的构建器!太棒了!

Now stuff is easy, I have to write a builder that takes request URI as a string and returns mock data, as a String! Brilliant!

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次...

It works fine! .... For the first request.
After the first request (/stuff/1.json) my MockRestServiceServer responds with message "Assertion error: no further requests expected".
Basically, I can make as many requests to that MockRestServiceServer as there were .expect() calls on it. And since I had only 1 of them, only first request will go through.
Is there a way around it? I really don't want to mock service C 10 or 20 times...

推荐答案

如果您查看MockRestServiceServer类,则它支持两个'expect()'方法.第一个默认值为'ExpectedCount.once()',但是第二个方法允许您更改此值

If you look at the MockRestServiceServer class, it supports two 'expect()' methods. The first defaults to 'ExpectedCount.once()' but the second method allows you change this value

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应该允许多次出现期望,其中概述了第二种方法的一些选项.

I found this ticket MockRestServiceServer should allow for an expectation to occur multiple times which outlines some options for second method.

在您的情况下,我认为添加静态导入并使用manyTimes()方法比for循环更整洁

In your case I think adding static import and using the manyTimes() method is neater code than the for loop

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

其他选项是

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

这篇关于Spring MockRestServiceServer处理对同一URI的多个请求(自动发现)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆