# SpringMockTestSample **Repository Path**: hadoobing/SpringMockTestSample ## Basic Information - **Project Name**: SpringMockTestSample - **Description**: Spring Test + Mockito - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 2 - **Created**: 2019-01-28 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Spring Mock Test ## Mock测试背景 ``` https://www.infoq.com/articles/stubbing-mocking-service-virtualization-differences ``` ### 正常情况下,Spring推荐使用接口interface, 所以一般依赖的都是接口 #### 定义一个接口 ``` public interface IBookService { /** * 根据类目,罗列出书单 * * @param category 书的类目 * @return 该类目下的书名 */ public List bookList(String category); } ``` #### 测试对象类里有依赖到这个接口 ``` @Autowired IBookService iBookService; ``` ### 在Spring启动的时候,会寻找接口的实现类 #### 如果没有找到,会报错 * 当依赖在第三方jar包里,测试时没有引入jar; * 或者没有扫实现类的package; ``` No qualifying bean of type 'com.github.yuxiaobin.mock.api.IBookService' available ``` #### 然而,该接口的实现可能依赖一大堆逻辑,或者实现类在第三方jar包里 #### 一般正常测试,必须要配置扫描路径/加载第三方jar依赖; 加载/依赖进来会导致: * 依赖的jar可能需要依赖一大堆其他jar,还可能跟现有依赖的jar冲突; * 可能需要依赖其他资源:数据库,redis,甚至是微服务; * 掉入依赖地狱; 而可能我们想测试的逻辑,跟依赖的bean关系不大,或者这个依赖的实现需要起一个微服务来被调用,而调用的结果也不算太重要, ### 那么问题来了: ``` 本来测试一段逻辑没几行代码,但是为测试准备环境需要一大堆,这让测试变得困难,效率大大降低 ``` ## Mock测试来帮你解决依赖地狱 ### 测试工具:[mockito](https://github.com/mockito/mockito) ``` org.mockito mockito-core 2.23.4 ``` ### 对于有实现类的,但不关心里面的逻辑 ``` @Mock IAddressService addressService;//这个类的逻辑做mock @Autowired IUserService userService;//待测试的类 ``` ``` @Before public void initMocks() throws Exception { MockitoAnnotations.initMocks(this); //模拟逻辑返回 when(addressService.getAddressByUserId(any(String.class))).thenReturn(new Address().setCountry("ZH")); //把mock的对象注入到需要测试的类中 org.springframework.test.util.ReflectionTestUtils.setField(userService, "addressService", addressService); } ``` ``` @Test public void mockInjectTest(){ User user = userService.getUserById("10086"); Assert.assertEquals("ZH", user.getAddress().getCountry()); } ``` ### 对于强制依赖了第三方接口,但又不太不想加载/不太关心第三方实现的结果 * 强制依赖: @Autowired(required=true) //默认就是true * 没有实现类,或者实现类是其他模块/服务,不想加载/起对应微服务 1. 需要在application-test.xml配置如下,不然没法启动spring容器(注意:factory-method="mock") ``` ``` 2. 测试类: ``` @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:applicationContext-mock-inject.xml"}) public class MockSpringTest { //@InjectMocks //IUserService userService = new UserServiceImpl(); //上面@InjectMocks,和@Autowired 两种写法都可以 @Autowired IUserService userService; @Mock IBookService iBookService;//其实并没有加载它默认的实现类/jar @Mock IAddressService addressService;//不太关心该api的结果 @Before public void initMock(){ //模拟这个第三方api返回结果集 when(iBookService.getBookListByUserId(any(String.class))).thenReturn(new ArrayList()); //通过反射,把iBookService注入进去 org.springframework.test.util.ReflectionTestUtils.setField(userService, "iBookService", iBookService); when(addressService.getAddressByUserId(any(String.class))).thenReturn(new Address().setCountry("ZH")); ReflectionTestUtils.setField(userService, "addressService", addressService); } @Test public void test()}{ User user = userService.getUserById("10086"); List bookList = user.getBookList(); Assert.assertEquals("book1", bookList.get(0)); Assert.assertEquals("book2", bookList.get(1)); Assert.assertEquals("ZH", user.getAddress().getCountry()); } } ``` ### 如果没有强制依赖第三方接口: @Autowired(required=false) 可以省略application-test.xml的配置 测试代码没什么变动 ## 总结一下: 1. 强制依赖接口,需要在xml里面使用工厂方法mock接口 2. 非强制依赖,可以无需上面的步骤; 3. 测试类使用@Mock注解来对接口进行模拟逻辑 4. 把mock的接口,注入到需要测试的对象里