微服务中mock掉feign Client进行restful单元测试

今天分享一个在微服务中restful接口的单元测试例子,主要涉及三方面:内部调用的mock、接口DTO的序列化和反序列化、用MockMvc加速单元测试用例执行。

  1. 单元测试要求不依赖外部服务,这样才能够方便的支持在各种环境下执行,特别是CI环境。但是在微服务开发中最常见的依赖就是服务间的调用。好在有Mockito,我们可以方便的实现各种stub来mock掉feign client的调用。

  2. 为了尽可能的测试到微服务的调用过程,我们还需要模拟DTO的序列号和反序列化过程。在一个restful接口的单元测试中序列化过程如下,保证请求和响应结构都会经历序列化和反序列化过程:
    请求数据序列化-->请求数据发序列化
    响应数据序列化-->响应数据反序列化

  3. MockMvc可以不启动http服务完成模拟rest的服务处理请求。因此一般使用MockMvc模拟restful服务来加速单元测试的执行。

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);
}

上面是controller、service和feign client,下面是测试类,通过AutoConfigureMockMvc注解自动配置MockMvc,会加载当前包的所有controller到MockMvc内。通过注入WebApplicationContext然后用其来获取容器中的bean,方便再单元测试开始前用反射将feignClient的字段替换成mock对象。再单元测试内使用Mockito.when(userApiClient.profile(Mockito.any())).thenReturn(new ProfileResp("0000","success"));插入stub使替换掉的userApiClient再调用profile接口时返回我们需要的结果。
objectMapper则是用来模拟spring web的jackson序列化和反序列化操作。
完整代码如下:
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());
    }
}

提示:本文为了方便阅读省略了import等代码。本例子使用的是junit5。


 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值