# Spring Mock Test
## Mock测试背景
```
https://www.infoq.com/articles/stubbing-mocking-service-virtualization-differences
```
### 正常情况下,Spring推荐使用接口interface, 所以一般依赖的都是接口
#### 定义一个接口
public interface IBookService { /** * 根据类目,罗列出书单 * * @param category 书的类目 * @return 该类目下的书名 */ public List<String> 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)
```
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.23.4</version>
</dependency>
```
### 对于有实现类的,但不关心里面的逻辑
@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")
<bean name="iBookService" class="org.mockito.Mockito" factory-method="mock"> <constructor-arg value="com.github.yuxiaobin.mock.api.IBookService"/> </bean>
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<String>()); //通过反射,把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<String> 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的配置
测试代码没什么变动
```
原创:https://www.cnblogs.com/tomcatandjerry/
源码地址: https://gitee.com/hadoobing/SpringMockTestSample
```
## 总结一下:
1. 强制依赖接口,需要在xml里面使用工厂方法mock接口
2. 非强制依赖,可以无需上面的步骤;
3. 测试类使用@Mock注解来对接口进行模拟逻辑
4. 把mock的接口,注入到需要测试的对象里