Today, I share a unit test example of restful interface in microservice, which mainly involves three aspects: mock of internal call, serialization and deserialization of interface DTO, and accelerating the execution of unit test cases with MockMvc.
-
Unit testing requires that it does not rely on external services, so that it can be easily executed in various environments, especially CI environment. However, the most common dependency in microservice development is the invocation between services. Fortunately, with Mockito, we can easily implement various stub s to mock the call of feign client.
-
In order to test the calling process of microservices as much as possible, we also need to simulate the sequence number and deserialization process of DTO. In the unit test of a restful interface, the serialization process is as follows to ensure that the request and response structures will undergo serialization and deserialization processes:
Request data serialization -- > request data serialization
Response data serialization -- > response data deserialization -
MockMvc can complete the service processing request simulating rest without starting the http service. Therefore, MockMvc is generally used to simulate restful services to speed up the execution of unit tests.
HelloController.java
@RestController public class HelloController { @Autowired private HelloService helloService; @PostMapping("/user/profile") public ProfileResp UserProfile(ProfileReq req) { return helloService.profile(req); } }
HelloService.java
@Service public class HelloService { @Autowired private UserApiClient userApiClient; public ProfileResp profile(ProfileReq req) { return userApiClient.profile(req); } }
UserApiClient.java
@FeignClient(name = "UserApiClient", path = "/user") public interface UserApiClient { @PostMapping("/profile") ProfileResp profile(ProfileReq req); }
The above is controller, service and feign client, and the following is test class. Automatically configure MockMvc through AutoConfigureMockMvc annotation, and all controllers of the current package will be loaded into MockMvc. By injecting WebApplicationContext and then using it to obtain the bean s in the container, it is convenient to replace the feignClient field with a mock object with reflection before the unit test starts. Then use mockito in the unit test when(userApiClient.profile(Mockito.any())). thenReturn(new ProfileResp("0000","success")); Insert the stub to make the replaced userapiclient return the results we need when calling the profile interface again.
objectMapper is used to simulate jackson serialization and deserialization of spring web.
The complete code is as follows:
DemoApplicationTests.java
@SpringBootTest @AutoConfigureMockMvc class DemoApplicationTests { @Autowired private WebApplicationContext webApplicationContext; @Autowired private ObjectMapper objectMapper; @Autowired private MockMvc mockMvc; @Mock private UserApiClient userApiClient; @BeforeEach void mockFeignClient() throws NoSuchFieldException, IllegalAccessException { HelloService helloService = webApplicationContext.getBean(HelloService.class); Field fieldUserApiClient = HelloService.class.getDeclaredField("userApiClient"); fieldUserApiClient.setAccessible(true); fieldUserApiClient.set(helloService, this.userApiClient); } @Test void userProfile() throws Exception { Mockito.when(userApiClient.profile(Mockito.any())) .thenReturn(new ProfileResp("0000","success")); ProfileReq req = ProfileReq.builder().uid("1111").build(); byte[] data = mockMvc.perform(MockMvcRequestBuilders.post("/user/profile") .content(objectMapper.writeValueAsBytes(req))) .andExpect(MockMvcResultMatchers.status().isOk()) .andReturn().getResponse().getContentAsByteArray(); ProfileResp resp = objectMapper.readValue(data, ProfileResp.class); Assertions.assertNotNull(resp); Assertions.assertEquals("0000", resp.getCode()); } }
Tip: in order to facilitate reading, this article omits the code such as import. This example uses junit5.