让我们说我正在为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作为字符串并返回模拟数据,作为String!辉煌!
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响应消息&#34;断言错误:没有预期的进一步请求&#34;。
基本上,我可以向MockRestServiceServer发出尽可能多的请求,因为它有.expect()调用。因为我只有其中一个,所以只有第一个请求会通过
有办法解决吗?我真的不想模拟服务C 10或20次......
答案 0 :(得分:22)
如果你看一下MockRestServiceServer类,它支持两个&#39; expect()&#39;方法。第一个默认为&#39; ExpectedCount.once()&#39;但第二种方法允许你改变这个值
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 should allow for an expectation to occur multiple times,其中概述了第二种方法的一些选项。
在你的情况下,我认为添加静态导入和使用manyTimes()方法比for循环更简洁
MockRestServiceServer
.expect(manyTimes(), requestContainsUri("/stuff"))
.andExpect(method(HttpMethod.GET))
其他选项
once();
manyTimes();
times(5);
min(2);
max(8);
between(3,6);
答案 1 :(得分:10)
编辑:请参阅@emeraldjava的回答,其中显示了Spring 4.3+用户的正确解决方案。
不幸的是,没有任何漂亮的机制可以期待多次调用。您可以手动执行也可以使用循环,例如:
for (int i = 0; i < 10; i++) {
mockRestServiceServer
.expect(requestContainsUri("/stuff"))
.andExpect(method(HttpMethod.GET))
.andRespond(withAutoDetectedCannedData());
}
请注意,必须在不中断的情况下调用请求,例如不能有另一个与“/ stuff”URI不匹配的REST调用。