Mockito摆脱测试的依赖地狱, 简化测试

# 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的接口,注入到需要测试的对象里

原文地址:https://www.cnblogs.com/tomcatandjerry/p/10330306.html