Mocking a RestTemplate in Spring – 在Spring中模拟一个RestTemplate

最后修改: 2018年 11月 5日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

We frequently find ourselves with applications that perform some sort of web request. When it comes to testing this behavior, we have a few options with Spring apps.

我们经常发现自己有一些执行某种网络请求的应用程序。在测试这种行为时,我们有几个选项与Spring应用程序。

In this quick tutorial, we’ll look at just a couple of ways of mocking such calls performed only through a RestTemplate.

I在这个快速教程中,我们将看一下只通过RestTemplate执行的这种调用的几种嘲弄方式。

We’ll start by testing with Mockito, a popular mocking library. Then we’ll use Spring Test, which provides us with a mechanism to create a mock server to define the server interactions.

我们将首先使用Mockito进行测试,这是一个流行的模拟库。然后我们将使用Spring Test,它为我们提供了一个创建模拟服务器的机制来定义服务器的交互。

2. Using Mockito

2.使用Mockito

We can use Mockito to mock the RestTemplate altogether. With this approach, testing our service would be as simple as any other test involving mocking.

我们可以使用Mockito来模拟整个RestTemplate。通过这种方法,测试我们的服务将和其他涉及嘲弄的测试一样简单。

Let’s assume we have a simple EmployeeService class, which fetches employee details through HTTP:

让我们假设我们有一个简单的EmployeeService类,它通过HTTP获取雇员的详细信息。

@Service
public class EmployeeService {
    
    @Autowired
    private RestTemplate restTemplate;

    public Employee getEmployee(String id) {
	ResponseEntity resp = 
          restTemplate.getForEntity("http://localhost:8080/employee/" + id, Employee.class);
        
	return resp.getStatusCode() == HttpStatus.OK ? resp.getBody() : null;
    }
}

Now let’s implement our test for the previous code:

现在让我们来实现我们对前面代码的测试:

@ExtendWith(MockitoExtension.class)
public class EmployeeServiceTest {

    @Mock
    private RestTemplate restTemplate;

    @InjectMocks
    private EmployeeService empService = new EmployeeService();

    @Test
    public void givenMockingIsDoneByMockito_whenGetIsCalled_shouldReturnMockedObject() {
        Employee emp = new Employee(“E001”, "Eric Simmons");
        Mockito
          .when(restTemplate.getForEntity(
            “http://localhost:8080/employee/E001”, Employee.class))
          .thenReturn(new ResponseEntity(emp, HttpStatus.OK));

        Employee employee = empService.getEmployee(id);
        Assertions.assertEquals(emp, employee);
    }
}

In the above JUnit test class, we first asked Mockito to create a dummy RestTemplate instance using the @Mock annotation.

在上面的JUnit测试类中,我们首先要求Mockito使用@Mock注解创建一个假的RestTemplate实例。

Then we annotated the EmployeeService instance with @InjectMocks to inject the dummy instance into it.

然后我们用@InjectMocks注解了EmployeeService实例,将假的实例注入其中。

Finally, in the test method, we defined the behavior of our mock using Mockito’s when/then support.

最后,在测试方法中,我们使用Mockito的when/then支持来定义我们的mock的行为。

3. Using Spring Test

3.使用Spring测试

The Spring Test module includes a mock server named MockRestServiceServer. With this approach, we configure the server to return a particular object when a specific request is dispatched through our RestTemplate instance. In addition, we can verify() on that server instance whether or not all expectations have been met.

Spring测试模块包括一个名为MockRestServiceServer的模拟服务器。 通过这种方法,我们将服务器配置为在通过我们的RestTemplate实例分派特定请求时返回一个特定对象。此外,我们可以在该服务器实例上验证()是否满足所有期望。

MockRestServiceServer actually works by intercepting the HTTP API calls using a MockClientHttpRequestFactory. Based on our configuration, it creates a list of expected requests and corresponding responses. When the RestTemplate instance calls the API, it looks up the request in its list of expectations, and returns the corresponding response.

MockRestServiceServer实际上是通过使用MockClientHttpRequestFactory来拦截HTTP API调用。基于我们的配置,它创建了一个预期请求和相应响应的列表。当RestTemplate实例调用API时,它在其预期列表中查找请求,并返回相应的响应。

Thus, it eliminates the need of running an HTTP server in any other port for sending mock responses.

因此,它消除了在任何其他端口运行HTTP服务器以发送模拟响应的必要性。

Let’s create a simple test for the same getEmployee() example using MockRestServiceServer:

让我们使用MockRestServiceServer为同一个getEmployee()例子创建一个简单测试。

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = SpringTestConfig.class)
public class EmployeeServiceMockRestServiceServerUnitTest {

    @Autowired
    private EmployeeService empService;
    @Autowired
    private RestTemplate restTemplate;

    private MockRestServiceServer mockServer;
    private ObjectMapper mapper = new ObjectMapper();

    @BeforeEach
    public void init() {
        mockServer = MockRestServiceServer.createServer(restTemplate);
    }
    
    @Test                                                                                          
    public void givenMockingIsDoneByMockRestServiceServer_whenGetIsCalled_thenReturnsMockedObject()() {   
        Employee emp = new Employee("E001", "Eric Simmons");
        mockServer.expect(ExpectedCount.once(), 
          requestTo(new URI("http://localhost:8080/employee/E001")))
          .andExpect(method(HttpMethod.GET))
          .andRespond(withStatus(HttpStatus.OK)
          .contentType(MediaType.APPLICATION_JSON)
          .body(mapper.writeValueAsString(emp))
        );                                   
                       
        Employee employee = empService.getEmployee(id);
        mockServer.verify();
        Assertions.assertEquals(emp, employee);                                                        
    }
}

In the previous snippet, we used static methods from in MockRestRequestMatchers and MockRestResponseCreators to define the expectation and response for the REST call in a clear and readable way:

在前面的片段中,我们使用了MockRestRequestMatchersMockRestResponseCreators中的静态方法,以清晰可读的方式定义REST调用的期望和响应。

import static org.springframework.test.web.client.match.MockRestRequestMatchers.*;      
import static org.springframework.test.web.client.response.MockRestResponseCreators.*;

We should keep in mind that the RestTemplate in the test class should be the same instance used in the EmployeeService class. To ensure this, we defined a RestTemplate bean in the spring config and auto-wired the instance in both test and implementation:

我们应该记住,测试类中的RestTemplate应该是EmployeeService类中使用的同一个实例。为了确保这一点,我们在spring config中定义了一个RestTemplate bean,并在测试和实现中自动连接了这个实例。

@Bean
public RestTemplate restTemplate() {
    return new RestTemplate();
}

Using a MockRestServiceServer is very useful when we write our integration tests and only need to mock external HTTP calls.

使用MockRestServiceServer是非常有用的,当我们编写集成测试,只需要模拟外部HTTP调用。

4. Conclusion

4.总结

In this brief article, we discussed a few effective options for mocking the external REST API calls over HTTP while writing unit tests.

在这篇简短的文章中,我们讨论了在编写单元测试时通过HTTP模拟外部REST API调用的几个有效选项。

The source code for the above article is available over on GitHub.

上述文章的源代码可在GitHub上找到over