1. 简介
在日常开发中,我们经常需要编写执行Web请求的应用程序。当我们对这类行为进行测试时,在Spring应用中有多种选择。
✅ 本文将介绍几种只通过 RestTemplate 执行调用的模拟方法。
我们将从使用 Mockito 这一流行的模拟库开始;接着使用 Spring Test 提供的机制,创建一个模拟服务器来定义服务交互行为。
2. 使用 Mockito 模拟 RestTemplate
Mockito 可以直接模拟整个 RestTemplate 实例。这种方式下,测试我们的服务就像测试任何其他涉及模拟对象的方法一样简单。
假设我们有一个简单的 EmployeeService
类,它通过 HTTP 获取员工信息:
@Service
public class EmployeeService {
@Autowired
private RestTemplate restTemplate;
public Employee getEmployee(String id) {
ResponseEntity<Employee> resp =
restTemplate.getForEntity("http://localhost:8080/employee/" + id, Employee.class);
return resp.getStatusCode() == HttpStatus.OK ? resp.getBody() : null;
}
}
接下来是对应的单元测试代码:
@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("E001");
Assertions.assertEquals(emp, employee);
}
}
在这个 JUnit 测试类中:
- 我们使用
@Mock
注解创建了一个虚拟的 RestTemplate 实例; - 使用
@InjectMocks
将该实例注入到EmployeeService
中; - 在测试方法中通过 Mockito 的 when/then 语法 定义了模拟行为。
⚠️ 注意:这种方式适用于纯粹的单元测试场景,不需要启动任何实际的HTTP服务。
3. 使用 Spring Test 模块中的 MockRestServiceServer
Spring Test 模块提供了一个名为 MockRestServiceServer 的模拟服务器。通过这个工具,我们可以配置当某个特定请求通过 RestTemplate 发出时,返回指定的响应。此外,还可以通过 verify()
方法验证所有预期是否被满足。
MockRestServiceServer 的工作原理是通过拦截 HTTP API 调用(基于 MockClientHttpRequestFactory),根据我们配置的规则匹配请求并返回预设的响应。
✅ 这种做法的好处在于无需启动真实的 HTTP 服务就能完成对外部接口的模拟测试。
下面是使用 MockRestServiceServer 对前面示例进行测试的代码:
@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() throws Exception {
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("E001");
mockServer.verify();
Assertions.assertEquals(emp, employee);
}
}
上述代码中,我们使用了来自 MockRestRequestMatchers
和 MockRestResponseCreators
的静态方法,清晰地定义了 REST 请求的期望与响应:
import static org.springframework.test.web.client.match.MockRestRequestMatchers.*;
import static org.springframework.test.web.client.response.MockRestResponseCreators.*;
⚠️ 重要提醒:为了确保 MockRestServiceServer 能正确拦截请求,测试类中使用的 RestTemplate 必须和 EmployeeService
中的是同一个实例。
为此,我们在 Spring 配置类中定义了一个 RestTemplate Bean,并在测试和实现类中都通过自动装配获取:
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
✅ 总结一句:当你写集成测试但只想模拟外部 HTTP 接口时,使用 MockRestServiceServer 是非常合适的方案。
4. 结论
本文简要介绍了在编写单元测试时模拟外部 REST API 调用的有效方式。
你可以根据测试目标选择合适的策略:
- 如果是纯单元测试,推荐使用 Mockito;
- 如果需要模拟真实网络调用、但仍不想启动外部服务,推荐使用 Spring 的 MockRestServiceServer。
源码可在 GitHub 查看:https://github.com/eugenp/tutorials/tree/master/spring-web-modules/spring-resttemplate