SpringBoot单元测试(三)Mockito

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhanshenzhi2008/article/details/85469194

描述

上一篇文章写了几个简单MockMVC的测试类,可以简单了解下MockMVC的用法。这篇文章主要是用来介绍Mockito的用法。它可以通过模拟对象来执行你需要的测试行为。

模拟外部依赖和模拟插入代码
执行测试代码
校验代码执行的正确性

Mock介绍

Mock,从字面上就知道是模拟的意思。其实它就是创建一个虚拟的对象,然后在测试环境中代替真实的对象,以达到最终的测试目的。借用官方通用的说法:

  1. 验证这个对象的某些方法的调用情况,调用了多少次,参数是什么等等
  2. 指定这个对象的某些方法的行为,返回特定的值,或者是执行特定的动作

如果有开发前端的同行们都知道,Mock.js是前后端分离必不可少的一门技术。

Mockito介绍

Mockito是Mock框架里应用最广泛的,也是java的一个应用。市面上还有Powermock for Java、GoogleMock for C++、Mock Server、WireMock、MockMVC。

Mockito的使用

  • 依赖

    <!-- SpringBoot框架的测试包,自动会引入这些Mock相关的包-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    
    <!-- 如果不是Spring framwork或纯java框架,要引入Junit包,且mockito-core -->
    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-core</artifactId>
        <scope>test</scope>
    </dependency>
    
  • 创建Mock对象

    1. 使用静态的mock()方法

    2. 使用@Mock注解

      注意的一点是,如果使用@Mock注解方法,你必须要触发@Mock注解对象的创建。

      • 使用MockitoAnnotations.initMocks(this)方法触发所有的@Mock注解对象的创建
      /**
       * MockitoTest
       *
       * @author orjrs
       * @date 2018-12-31 11:15
       */
      public class MockitoTest {
          @Mock
          private GirlValidateService girlValidateService;
      
          @Before // @Test注解的方法运行前执行,触发所有的@Mock注解对象的创建
          public void setUp() {
              MockitoAnnotations.initMocks(this);
          }
      
      }
      
      • 或者使用 @RunWith(MockitoJUnitRunner.class)注解到类里

        /**
         * MockitoTest
         *
         * @author orjrs
         * @date 2018-12-31 11:15
         */
        @RunWith(MockitoJUnitRunner.class) // 可以自动触发所有的@Mock注解对象的创建
        public class MockitoTest {
            @Mock
            private GirlValidateService girlValidateService;
        
        //    @Before // @Test注解的方法运行前执行,触发所有的@Mock注解对象的创建
        //    public void setUp() {
        //        MockitoAnnotations.initMocks(this);
        //    }
        
        }
        
        • 或者使用MockitoRule

          扫描二维码关注公众号,回复: 4728070 查看本文章
          /**
           * MockitoTest
           *
           * @author orjrs
           * @date 2018-12-31 11:15
           */
          // @RunWith(MockitoJUnitRunner.class) // 可以自动触发所有的@Mock注解对象的创建
          public class MockitoTest {
              @Mock
              private GirlValidateService girlValidateService;
          
              @Rule  // 可以自动触发所有的@Mock注解对象的创建
              public MockitoRule mockitoRule = MockitoJUnit.rule();
          
          //    @Before // @Test注解的方法运行前执行,触发所有的@Mock注解对象的创建
          //    public void setUp() {
          //        MockitoAnnotations.initMocks(this);
          //    }
          
          }
          

​ 完整的例子(GirlValidateServiceImpl和GirlServiceImpl后面再继续贴了):

package com.orjrs.spring.test.service.impl;

import com.orjrs.spring.test.model.Girl;
import com.orjrs.spring.test.service.GirlService;
import com.orjrs.spring.test.service.GirlValidateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

/**
 * GirlValidateServiceImpl
 *
 * @author orjrs
 * @date 2018-12-31 11:40
 */
@Service("girlValidateService")
public class GirlValidateServiceImpl implements GirlValidateService {
    private final GirlService girlService;

    @Autowired
    public GirlValidateServiceImpl(GirlService girlService) {
        this.girlService = girlService;
    }

    @Override
    public boolean validate(String name) {
        if (StringUtils.isEmpty(name)) {
            return false;
        }
        Girl girl = girlService.queryGirl(1L);
        if (girl != null && name.equals(girl.getName())) {
            return true;
        }
        return false;
    }
}
package com.orjrs.spring.test.service.impl;

import com.orjrs.spring.test.model.Girl;
import com.orjrs.spring.test.service.GirlService;
import org.springframework.stereotype.Service;

/**
 * GirlServiceImpl
 *
 * @author orjrs
 * @date 2018-12-31 12:25
 */
@Service("girlService")
public class GirlServiceImpl implements GirlService {
    @Override
    public Girl queryGirl(Long id) {
        Girl girl = new Girl();
        girl.setId(1L);
        girl.setName("张女士");
        girl.setAge(23);
        if (id.equals(girl.getId())) {
            return girl;
        }
        return null;
    }
}
/**
 * GirlValidateServiceTest
 *
 * @author orjrs
 * @date 2018-12-31 12:20
 */
public class GirlValidateServiceTest extends MockitoTest {// MockitoTest 参考上面其中任何一个
    @Mock
    private GirlService girlService;

    @Test
    public void validate() {
        // 创建实例,mock对象作为构造函数的参数
        GirlValidateService girlValidateService = new GirlValidateServiceImpl(girlService);
        
        // 该方法体内调用GirlService的方法,此时@Mock出来的虚拟对象,是不会调用其实际方法
        // girlService.queryGirl,所以返回的是null,导致判断为false
        boolean flag = girlValidateService.validate("张女士");
        assertFalse(flag);
        Mockito.verify(girlService).queryGirl(1L); // 验证queryGirl调用是通过Mock对象的,这个方法的结果为null,  girlService.queryGirl实际方法我已经写死了:1L 张女士 23
    }
}
  • 配置模拟
  1. when thenReturn 和 when thenThrow

    简单来说,当调用方法时,然后再返回自己设值或抛出自己设定异常

    thenReturn 或 thenThrow
    When
    结果
    /**
     * GirlValidateServiceTest
     *
     * @author orjrs
     * @date 2018-12-31 12:20
     */
    public class GirlValidateServiceTest extends MockitoTest {
        @Mock
        private GirlService girlService;
    
        @Test
        public void validate() {
            // 创建实例,mock对象作为构造函数的参数
            GirlValidateService girlValidateService = new GirlValidateServiceImpl(girlService);
           
    
            // 2. 配置mocks的测试:如果调用这个方法,始终返回一个自定义的对象
            Mockito.when(girlService.queryGirl(1L)).thenReturn(new Girl() {{
                setId(0L);
                setName("张女士");
            }}); // girlService.queryGirl实际方法我已经写死了:1L 张女士 23
            assertTrue(girlValidateService.validate("张女士")); // // 验证校验方法
        }
    }
    
  • doReturn when 和 doThrow when

它和when thenReturn 和when thenThrow很相似。它对于spy的用法来说是非常有用的。因为spy和mock的区别在于:前者会调用实际的方法,而mock不会。

请看下面关于spy的介绍,我会详细说明这个方法的作用。

package com.orjrs.spring.test.service;

import com.orjrs.spring.test.model.Girl;
import com.orjrs.spring.test.service.impl.GirlValidateServiceImpl;
import com.orjrs.spring.test.unit.MockitoTest;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.Spy;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

/**
 * GirlValidateServiceTest
 *
 * @author orjrs
 * @date 2018-12-31 12:20
 */
public class GirlValidateServiceTest extends MockitoTest {
    @Mock
    private GirlService girlService;

    @Spy // 如果不用@Spy注解,则可以Mockito.spy(spyGirlService)
    private GirlService spyGirlService;
    
    @Test
    public void testSpyWrong() {
        // mock 一个LinkedList
        List<Girl> list = new LinkedList<>();
        List<Girl> spy = Mockito.spy(list);

        // 这个方法不会执行,因为spy的对象会去执行其实际方法,即spy.get(0)会去调用实际的方法,而list目前一个empty,则get(0)会异常
        Mockito.when(spy.get(0)).thenReturn(new Girl() {{
            setId(0L);
            setName("李女士");
        }});
        // 上面一句会报错,这里不会执行
        assertEquals("李女士", spy.get(0).getName());
    }

    @Test
    public void testSpyCorrect() {
        // mock 一个LinkedList
        List<Girl> list = new LinkedList<>();
        List<Girl> spy = Mockito.spy(list);

        // 这个方法不会执行,因为spy的对象会去执行其实际方法,即spy.get(0)会去调用实际的方法,而list目前一个empty,则get(0)会异常
        // 但doReturn when方法和when thenReturn不一样,.when(spy)并不会报错,调用get(0)它始终会返回一个结果
        Mockito.doReturn(new Girl() {{
            setId(0L);
            setName("李女士");
        }}).when(spy).get(0); // .when(spy).get(0);如果改成 when(spy.get(0)),一样会报错
        // 会执行这行代码
        assertEquals("李女士", spy.get(0).getName());
    }
}
  • 使用@InjectMocks依赖注入

    @InjectMocks注解试图将构造函数,方法或字段通过基于类型来依赖注入。例如,假设你有下面的类。

    注意:一定是基于类型,比如下面@InjectMocks // 创建一个实例,并注入@Mock的实例
    ​ private GirlValidateServiceImpl girlValidateServiceImpl;换成 GirlValidateServiceI接口就会报错

    package com.orjrs.spring.test.service;
    
    import com.orjrs.spring.test.model.Girl;
    import com.orjrs.spring.test.service.impl.GirlValidateServiceImpl;
    import com.orjrs.spring.test.unit.MockitoTest;
    import org.junit.Test;
    import org.mockito.*;
    
    import java.util.LinkedList;
    import java.util.List;
    
    import static org.junit.Assert.*;
    
    /**
     * GirlValidateServiceTest
     *
     * @author orjrs
     * @date 2018-12-31 12:20
     */
    public class GirlValidateServiceTest extends MockitoTest {
        @Mock
        private GirlService girlService;
    
        @Spy
        private GirlService spyGirlService;
    
        @InjectMocks // 创建一个实例,并注入@Mock的实例
        private GirlValidateServiceImpl girlValidateServiceImpl;
    
        @Test
        public void validate() {
            // 创建实例,mock对象作为构造函数的参数
            //GirlValidateService girlValidateService = new //GirlValidateServiceImpl(girlService);
            // 可以直接调用imGirlValidateServiceg,不需要上面的初始化
            imGirlValidateServiceg.validate("张女士");
    
        }
    
       
    
    }
    
  • 局限性

Mockito不能模拟静态方法和私有方法。

猜你喜欢

转载自blog.csdn.net/zhanshenzhi2008/article/details/85469194