使用 MockRestServiceServer 时无法精确测试服务调用次数

Posted

技术标签:

【中文标题】使用 MockRestServiceServer 时无法精确测试服务调用次数【英文标题】:When using MockRestServiceServer cannot precisely test number of service calls 【发布时间】:2019-02-21 18:19:45 【问题描述】:

我正在为服务调用编写一些重试逻辑,并尝试在单元测试中测试 Rest Template 是否尝试命中服务一定次数。我正在使用以下代码来执行测试。

MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build();
mockServer.expect(ExpectedCount.times(5), method(HttpMethod.GET))
  .andRespond(withServerError());

service.call();

我将重试逻辑设置为仅进行两次尝试。上面的测试代码要求它发生五次,但测试总是通过。事实上,我可以让这个测试失败的唯一方法是将预期计数设置为 1(任何小于实际调用次数的值)。当我使用ExpectedCount.minExpectedCount.between 时会出现同样的问题,因为只有在实际调用超出预期时测试才会失败。

我需要能够测试确切数量的服务调用,最好不使用 Mockito。

【问题讨论】:

【参考方案1】:

这是最终对我有用的方法,最多尝试 4 次:

MockRestServiceServer server;

@Before
public void setUp() 
    server = MockRestServiceServer.bindTo(restTemplate).build();


@After
public void serverVerify() 
    server.verify();


@Test
public void doWork_retryThenSuccess() throws Exception 
    final String responseBody = "<some valid response JSON>";
    final String url = BASE_URL + "/doWork";
    server.expect(requestTo(url))
          .andExpect(MockRestRequestMatchers.method(HttpMethod.POST))
          .andRespond(ExceptionResponseCreator.withException(new SocketTimeoutException("first")));

    server.expect(requestTo(url))
          .andExpect(MockRestRequestMatchers.method(HttpMethod.POST))
          .andRespond(ExceptionResponseCreator.withException(new IOException("second")));

    server.expect(requestTo(url))
          .andExpect(MockRestRequestMatchers.method(HttpMethod.POST))
          .andRespond(ExceptionResponseCreator.withException(new RemoteAccessException("third")));

    server.expect(requestTo(url))
          .andExpect(MockRestRequestMatchers.method(HttpMethod.POST))
          .andRespond(withSuccess(responseBody, MediaType.APPLICATION_JSON));

    final MyResponseClass response = myService.call();

    assertThat(response, notNullValue());
    // other asserts here...

我们被限制使用 Spring Test 5.0.10,它没有MockRequestResponseCreators.withException()(方法是在 5.2.2 中添加的)。借用 Spring 5.2.7 代码,效果很好:

package com.company.test;

import java.io.IOException;

import org.springframework.remoting.RemoteAccessException;
import org.springframework.test.web.client.ResponseCreator;
import org.springframework.test.web.client.response.MockRestResponseCreators;

public class ExceptionResponseCreator extends MockRestResponseCreators 
    public static ResponseCreator withException(IOException ex) 
        return request ->  throw ex; ;
    

    public static ResponseCreator withException(RemoteAccessException ex) 
        return request ->  throw ex; ;
    

【讨论】:

【参考方案2】:

您需要在提出所有请求后致电mockServer.verify(),以检查是否符合预期。否则,您可以从不提出任何请求而侥幸逃脱。

【讨论】:

【参考方案3】:

您可以使用所需的逻辑创建自己的 ResponseCreator。例如:

class DelegateResponseCreator implements ResponseCreator 
    private final ResponseCreator[] delegates;
    private int toExecute = 0;

    public DelegateResponseCreator(final ResponseCreator... delegates) 
        this.delegates = delegates;
    

    @Override
    public ClientHttpResponse createResponse(final ClientHttpRequest request) throws IOException 
        ClientHttpResponse ret = this.delegates[this.toExecute % this.delegates.length].createResponse(request);
        this.toExecute++;

        return ret;
    


这个委托按顺序执行 ResponseDelegates。

所以你可以模拟你想要的电话号码的响应

mockServer.expect(ExpectedCount.times(5), MockRestRequestMatchers.method(HttpMethod.GET))
            .andRespond(new DelegateResponseCreator(
                    MockRestResponseCreators.withServerError(), 
                    MockRestResponseCreators.withServerError(), 
                    MockRestResponseCreators.withServerError(), 
                    MockRestResponseCreators.withServerError(), 
                    MockRestResponseCreators.withSuccess()
                    ));

在此示例中,前四个调用将返回服务器错误,而第五个调用将成功。

【讨论】:

检查 zed 的答案是否准确。

以上是关于使用 MockRestServiceServer 时无法精确测试服务调用次数的主要内容,如果未能解决你的问题,请参考以下文章

如何在 MockRestServiceServer 中模拟 http 标头?

如何在 MockRestServiceServer 中通过字符串模式期待 requestTo?

Spring MockRestServiceServer 处理多个异步请求

由于未绑定的 RestTemplate,Spring-Boot RestClientTest 无法正确自动配置 MockRestServiceServer

Spring Boot + 云 | Zuul 代理 |集成测试

如何管理在开发(而非测试)环境中使用多个微服务并对其进行模拟?