zoukankan      html  css  js  c++  java
  • 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的接口,注入到需要测试的对象里

  • 相关阅读:
    Mysql数据查询
    Mysql数据类型
    desc和show
    Mysql权限管理
    深入理解inode和硬链接和软连接和挂载点
    Linux用户和组
    VIM文本编辑器
    Linux文件操作
    MySQL基础
    八大排序
  • 原文地址:https://www.cnblogs.com/tomcatandjerry/p/10330306.html
Copyright © 2011-2022 走看看