基于Mockito的多层模拟单元测试 小结 淩亂°似流年 2022-01-28 08:13 712阅读 0赞 1.首先,我们先来了解下Mock是个什么东西吧。[Mockito教程][Mockito] 2.基本上一些简单unit-test的mock用法可以参照上面那个教程了。我要总结的主要是分为两个方面: 1) 通过Mock来访问Controller; 参考:[基于RESTful风格的SpringMVC的测试][RESTful_SpringMVC] , [Testing Spring MVC Controllers][] 2) 通过Mock来访问Controller,并且模拟Dao层数据入库功能。也就是在不改变原有代码的基础上,处理get请求且取消数据入库。 参考:[多层mock的单元测试][mock] 3.**前提**:我们的项目是MVC的架构,假设分为controller, service, dao。 1) 假设controller代码如下: package com.bas.controller; import com.bas.TestService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; @RestController public class TestController { @Autowired private TestService service; @RequestMapping(value = "/test/abc", method = RequestMethod.GET) public Boolean cfginstFileParse() { return service.parse("abc.xml"); } } 2) 假设service代码如下: package com.bas.serviceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @Service public class TestServiceImpl implements TestService { @Autowired private TestDao handler; @Value("${size}") private Integer size; @Override public Boolean parse(String xpath){ return handler.parseXml(); } } 3) 假设dao代码如下: package com.bas.dao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @Service public class TestDaoImpl implements TestDao { @Override public Boolean parseXml(String xpath){ return true; } } 4) MockUtil工具类: package com.bas.common.util; import com.bas.dao; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; import java.lang.reflect.Type; import java.util.HashMap; import java.util.List; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.mockito.Mockito.doNothing; public class MockUtil { private volatile static MockUtil mockUtil; private static MockMvc mvc; // process request controller public MockUtil() {} public static MockUtil builder(WebApplicationContext wac) { if(mockUtil == null) { synchronized (MockUtil.class){ if(mockUtil == null) { mockUtil = new MockUtil(); mvc = MockMvcBuilders.webAppContextSetup(wac).build(); } } } return mockUtil; } /** * Process the get request. * @param url controller restful api * @return return the result from controller * @throws Exception */ public static MvcResult getPerformResult(String url) throws Exception { return mvc.perform(MockMvcRequestBuilders .get(url)) .andExpect(status().isOk()) .andReturn(); } /** * Process the get request for parsing. * @param controller the app entrance we want to test * @param handler the object what is mocked , so it would not run * @param url controller restful api * @return return the result from controller * @throws Exception */ public static MvcResult parsePerformResult(Object controller, Dao handler, String url) throws Exception { // Setup Spring test in standalone mode MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); // stub the method handle , to do nothing when is's called. doNothing().when(handler).handle(new HashMap<String, List<Object>>()); return mockMvc.perform(MockMvcRequestBuilders .get(url)) .andExpect(status().isOk()) .andReturn(); } } 4.**通过Mock来访问Controller** package com.bas.test; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class Test { @Autowired private WebApplicationContext wac; private MockMvc mvc; @Before public void before() { mvc = MockMvcBuilders.webAppContextSetup(wac).build(); } @Test public void unitTest() { try { MvcResult result = MockUtil.builder(wac).getPerformResult("/test/abc"); System.out.println(result.getResponse().getContentAsString()); } catch (Exception e) { } } } 5.**通过Mock来访问Controller、dao层模拟的多层Mock** package com.bas.test; import com.bas.TestController; import com.bas.TestServiceImpl; import com.bas.TestDaoImpl; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MvcResult; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class Test { /* start to mock parse from controller api */ @Before public void before() { // initialize mock object , without it would cause in failed mock MockitoAnnotations.initMocks(this); } @Autowired private TestController testController; @InjectMocks @Autowired TestServiceImpl mockService; @Mock TestDaoImpl mockHandler; @Test public void mockControllerTest() { try { // ReflectionTestUtils.setField(mockService, "size", 1); // run controller , mock service and handler MvcResult result = MockUtil.parsePerformResult(testController, mockHandler, "/test/abc"); System.out.println(result.getResponse().getContentAsString()); }catch (Exception e) { } } } 注: 1.@InjectMocks, @Autowired 注入的是TestServiceImpl而不是TestService,因为Mock会创建一个实例而interface是不可以的; 2.MockitoAnnotations.initMocks(this); 如果这一句漏掉了会导致dao真实的执行而模拟失败; 3.细心的可以看到TestServiceImpl的@Value("$\{size\}"),这个如果在service用到的话需要在test这样注入:ReflectionTestUtils.setField(mockService, "size", 1)。 附: 1.[可模拟的数据类型 教程][Link 1] [Mockito]: https://www.jianshu.com/p/f6e3ab9719b9 [RESTful_SpringMVC]: https://www.jianshu.com/p/91045b0415f0 [Testing Spring MVC Controllers]: https://www.cnblogs.com/wade-xu/p/4311657.html [mock]: https://blog.csdn.net/weixin_34228387/article/details/86810729 [Link 1]: https://gitee.com/tang006/mock
还没有评论,来说两句吧...