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

查看:30
本文介绍了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,而是它的一部分:

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() 调用一样多的请求.而且由于我只有其中一个,因此只有第一个请求会通过.
有办法解决吗?我真的不想模拟服务 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天全站免登陆