• mockito使用教程


    一、什么是 Mock 测试

    Mock 测试就是在测试过程中,对于某些不容易构造(如 HttpServletRequest 必须在Servlet 容器中才能构造出来)或者不容易获取比较复杂的对象(如 JDBC 中的ResultSet 对象),用一个虚拟的对象(Mock 对象)来创建以便测试的测试方法。Mock 最大的功能是帮你把单元测试的耦合分解开,如果你的代码对另一个类或者接口有依赖,它能够帮你模拟这些依赖,并帮你验证所调用的依赖的行为。
    先来看看下面这个示例:


    从上图可以看出如果我们要对A进行测试,那么就要先把整个依赖树构建出来,也就是BCDE的实例。
    一种替代方案就是使用mocks


    从图中可以清晰的看出
    mock对象就是在调试期间用来作为真实对象的替代品。
    mock测试就是在测试过程中,对那些不容易构建的对象用一个虚拟对象来代替测试的方法就叫mock测试。

    二、Mockito是什么

    Mockito 是一个流行 mock 框架,可以和JUnit结合起来使用。Mockito 允许你创建和配置 mock 对象。使用Mockito可以明显的简化对外部依赖的测试类的开发。

    I notice that Mockito was voted "the best mock framework for Java" on Stackoverflow.

    三、使用方法

    1、添加Maven依赖

    <!-- mockito -->
    <dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>1.8.5</version>
    <scope>test</scope>
    </dependency>

    如果使用springboot的话,有自带可以不用引入

    2、建议静态导入会使代码更简洁

    import static org.mockito.Mockito.*;
    import static org.junit.Assert.*;

    3、例子

    3.1 创建一个mock对象,并校验

    @Test
    public void Demo1() {
    // mock creation 创建mock对象
    List mockedList = mock(List.class);
    
    //using mock object 使用mock对象
    mockedList.add("one");
    mockedList.clear();
    
    //verification 验证
    verify(mockedList).add("one");
    verify(mockedList).clear();
    }

    3.2 使用测试桩
    默认情况下,所有的函数都有返回值。mock函数默认返回的是null,一个空的集合或者一个被对象类型包装的内置类型,例如0、false对应的对象类型为Integer、Boolean;
    测试桩函数可以被覆写 : 例如常见的测试桩函数可以用于初始化夹具,但是测试函数能够覆写它。请注意,覆写测试桩函数是一种可能存在潜在问题的做法;
    一旦测试桩函数被调用,该函数将会一致返回固定的值;
    上一次调用测试桩函数有时候极为重要-当你调用一个函数很多次时,最后一次调用可能是你所感兴趣的。

    @Test
    public void Demo2() {
    // 你可以mock具体的类型,不仅只是接口
    LinkedList mockedList = mock(LinkedList.class);
    
    // 测试桩(可以使用连续调用)
    when(mockedList.get(0)).thenReturn("first");
    //when(mockedList.get(0)).thenReturn("first").thenReturn("second");
    when(mockedList.get(1)).thenThrow(new RuntimeException());
    
    // 输出“first”
    System.out.println(mockedList.get(0));
    // 连续调用则输出“second”
    //System.out.println(mockedList.get(0));
    
    // 抛出异常
    System.out.println(mockedList.get(1));
    
    // 因为get(999) 没有打桩,因此输出null
    System.out.println(mockedList.get(999));
    
    // 验证get(0)被调用的次数
    verify(mockedList).get(0);
    }

    3.3 验证函数的确切、最少、从未调用次数

    @Test
    public void Demo3() {
    // 你可以mock具体的类型,不仅只是接口
    LinkedList mockedList = mock(LinkedList.class);
    //using mock
    mockedList.add("once");
    
    mockedList.add("twice");
    mockedList.add("twice");
    
    mockedList.add("three times");
    mockedList.add("three times");
    mockedList.add("three times");
    
    // 下面的两个验证函数效果一样,因为verify默认验证的就是times(1)
    verify(mockedList).add("once");
    verify(mockedList, times(1)).add("once");
    
    // 验证具体的执行次数
    verify(mockedList, times(2)).add("twice");
    verify(mockedList, times(3)).add("three times");
    
    // 使用never()进行验证,never相当于times(0)
    verify(mockedList, never()).add("never happened");
    
    // 使用atLeast()/atMost()
    verify(mockedList, atLeastOnce()).add("three times");
    
    // mockedList.add("five times");
    // mockedList.add("five times");
    // mockedList.add("five times");
    // mockedList.add("three times");
    // mockedList.add("three times");
    // mockedList.add("three times");
    //最少or最多
    verify(mockedList, atLeast(2)).add("five times");
    verify(mockedList, atMost(5)).add("three times");
    
    }

    3.4 模拟异常

    @Test
    public void Demo4() {
    
    LinkedList mockedList = mock(LinkedList.class);
    doThrow(new RuntimeException()).when(mockedList).clear();
    
    // 调用这句代码会抛出异常
    mockedList.clear();
    }

    3.5 验证执行执行顺序

    @Test
    public void Demo5() {
    // A. 验证mock一个对象的函数执行顺序
    List singleMock = mock(List.class);
    
    singleMock.add("was added first");
    singleMock.add("was added second");
    singleMock.contains("111");
    singleMock.isEmpty();
    singleMock.remove(0);
    singleMock.get(0);
    
    // 为该mock对象创建一个inOrder对象
    InOrder inOrder = inOrder(singleMock);
    
    // 确保add函数首先执行的是add("was added first"),然后才是add("was added second")
    inOrder.verify(singleMock).add("was added first");
    inOrder.verify(singleMock).add("was added second");
    inOrder.verify(singleMock).contains("111");
    inOrder.verify(singleMock).isEmpty();
    inOrder.verify(singleMock).remove(0);
    inOrder.verify(singleMock).get(0);
    
    // // B .验证多个mock对象的函数执行顺序
    // List firstMock = mock(List.class);
    // List secondMock = mock(List.class);
    //
    // firstMock.add("was called first");
    // secondMock.add("was called second");
    //
    // // 为这两个Mock对象创建inOrder对象
    // InOrder inOrder = inOrder(firstMock, secondMock);
    //
    // // 验证它们的执行顺序
    // inOrder.verify(firstMock).add("was called first");
    // inOrder.verify(secondMock).add("was called second");
    
    // Oh, and A + B can be mixed together at will
    }

    3.6 spy
    然而很多时候,你希望达到这样的效果:除非指定,否者调用这个对象的默认实现,同时又能拥有验证方法调用的功能。这正好是spy对象所能实现的效果。创建一个spy对象,以及spy对象的用法介绍如下:

    @Test
    public void Demo6() {
    
    List list = new LinkedList();
    List spy = spy(list);
    
    // 你可以为某些函数打桩
    when(spy.size()).thenReturn(100);
    
    // 通过spy对象调用真实对象的函数
    spy.add("one");
    spy.add("two");
    
    // 输出第一个元素
    System.out.println(spy.get(0));
    
    // 因为size()函数被打桩了,因此这里返回的是100
    System.out.println(spy.size());
    
    // 交互验证
    verify(spy).add("one");
    verify(spy).add("two");
    }

    3.7 在监控对象上使用when(Object)来进行打桩是不可能或者不切实际的。
    因此,当使用监控对象时请考虑doReturn|Answer|Throw()函数族来进行打桩。

    @Test
    public void Demo7(){
    List list = new LinkedList();
    List spy = spy(list);
    
    // 不可能 : 因为当调用spy.get(0)时会调用真实对象的get(0)函数,此时会发生IndexOutOfBoundsException异常,因为真实List对象是空的
    //when(spy.get(0)).thenReturn("foo");
    
    // 你需要使用doReturn()来打桩
    doReturn("foo").when(spy).get(anyInt());
    
    System.out.println(spy.get(0));
    // doThrow(new RuntimeException()).when(spy).get(anyInt());
    // System.out.println(spy.get(0));
    }

    3.8使用注解形式来模拟测试对象,必须在初始化fields (领域),有2种方式初始化:

    @RunWith(@MockitoJUnitRunner.class) 标注 JUnit 测试类
    @Before 之前调用 MockitoAnnotations.initMocks(Object)
    @Before
    public void init(){
    MockitoAnnotations.initMocks(this);
    }

    或者

    @RunWith(MockitoJUnitRunner.class)

    这里补充一下几个注解的作用

    一般项目测试是用@RunWith(SpringRunner.class)

    这个运行器来启动的,有些教程使用的是SpringJUnit4ClassRunner这个运行器,我们可以查看源码,其实是同一个东西,

    public final class SpringRunner extends SpringJUnit4ClassRunner {
        public SpringRunner(Class<?> clazz) throws InitializationError {
            super(clazz);
        }
    }

    3.9 参数捕获

    ArgumentCaptor类允许我们在verification期间访问方法的参数。得到方法的参数后我们可以使用它进行测试。
    ArgumentCaptor是一个能够捕获参数值的特殊参数匹配器。捕获一个 Mock 对象的方法调用所传递的参数

    @Test
    public void testCaptureArgument() {
        List<String> list = Arrays.asList("1", "2");
        List mockedList = mock(List.class);
        ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
        mockedList.addAll(list);
        verify(mockedList).addAll(argument.capture());
    
        assertEquals(2, argument.getValue().size());
        assertEquals(list, argument.getValue());
    }

    3.10 RETURNS_SMART_NULLS 和 RETURNS_DEEP_STUBS

    RETURNS_SMART_NULLS

    (返回默认值-查看源码: ReturnsMoreEmptyValues)
    RETURNS_SMART_NULLS
    在创建mock对象时,有的方法我们没有进行stubbing,所以在调用的时候有时会返回Null这样在进行处理时就很可能抛出NullPointerException。如果通过RETURNS_SMART_NULLS参数来创建的mock对象在调用没有stubbed的方法时他将返回SmartNull。例如:返回类型是String它将返回空字符串””;是int,它将返回0;如果是List,它会返回一个空的List。另外,在堆栈中可以看到SmartNull的友好提示。 


    由于使用了RETURNS_SMART_NULLS参数来创建mock对象,所以在执行下面的操作时将不会抛出NullPointerException异常,另外堆栈也提示了相关的信息“SmartNull returned by unstubbed get() method on mock”。 

    @Test
    public void returnsSmartNullsTest() {
        //List mock = mock(List.class);
        List mock = mock(List.class, RETURNS_SMART_NULLS);
        System.out.println(mock.get(0));
    
        // 使用RETURNS_SMART_NULLS参数创建的mock对象,不会抛出NullPointerException异常。
        // 另外控制台窗口会提示信息“SmartNull returned by unstubbed get() method on mock”
        System.out.println(mock.toArray().length);
    }

    RETURNS_DEEP_STUBS

    (判断是否需要返回默认值,否则创建mock对象 ReturnsDeepStubs)
    RETURNS_DEEP_STUBS参数程序会自动进行mock所需的对象,方法deepstubsTest和deepstubsTest2是等价的

    public class FakeEntity {
        private UserFakeEntity userFakeEntity;
    
        public UserFakeEntity getUserFakeEntity() {
            return userFakeEntity;
        }
    
        public void setUserFakeEntity(UserFakeEntity userFakeEntity) {
            this.userFakeEntity = userFakeEntity;
        }
    }
    
    
    @Test
    public void deepstubsTest() {
        //FakeEntity fakeEntity = mock(FakeEntity.class);//这样会NullPointerException
        FakeEntity fakeEntity = mock(FakeEntity.class, RETURNS_DEEP_STUBS);
        when(fakeEntity.getUserFakeEntity().getName()).thenReturn("Beijing");
        System.out.println(fakeEntity.getUserFakeEntity().getName());
        assertEquals("Beijing", fakeEntity.getUserFakeEntity().getName());
    }
    
    @Test
    public void deepstubsTest2() {
        FakeEntity fakeEntity = mock(FakeEntity.class);
        UserFakeEntity userFakeEntity = mock(UserFakeEntity.class);
    
        when(fakeEntity.getUserFakeEntity()).thenReturn(userFakeEntity);
        when(userFakeEntity.getName()).thenReturn("Beijing");
        System.out.println(fakeEntity.getUserFakeEntity().getName());
        assertEquals("Beijing", fakeEntity.getUserFakeEntity().getName());
    }

    四、实战使用

    4.1登录的例子

    import org.junit.Before;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.mockito.InjectMocks;
    import org.mockito.Mock;
    import org.mockito.MockitoAnnotations;
    import org.mockito.runners.MockitoJUnitRunner;
    import org.springframework.boot.test.context.SpringBootTest;
    
    import static org.mockito.Mockito.*;
    
    
    //基于MockitoJUnitRunner的运行器
    @RunWith(MockitoJUnitRunner.class)
    //@RunWith(SpringRunner.class)
    @SpringBootTest
    public class MockLoginTest {
    
    
    //自动将模拟对象或侦查域注入到被测试对象中。
    //对被测类中@Autowired的对象,用@Mocks标注;对被测类自己,用@InjectMocks标注
    @InjectMocks
    private UserServiceImpl userService;
    
    @Mock
    private UserRepository userDao;
    
    @Mock
    private BargainBlackListService bargainBlackListService;
    
    @Mock
    private DistributionUserService distributionUserService;
    
    @Mock
    private AuthorityService authorityService;
    
    @Before
    public void init(){
    MockitoAnnotations.initMocks(this);
    }
    
    @Test
    public void loginTest() {
    //模拟方法动作
    //spyUserService在调用getUserEntity方法的时候,指定返回事先定义好的userEntity
    UserService spyUserService = spy(userService);
    UserEntity userEntity = new UserEntity();
    userEntity.setUserName("ppp");
    doReturn(userEntity).when(spyUserService).getUserEntity(anyString());
    when(bargainBlackListService.checkBlackList(anyString())).thenReturn(true);
    UserInfoDTO userInfoDTO = new UserInfoDTO();
    userInfoDTO.setUserName("ppp");
    userInfoDTO.setId("2c95808a644ade6801644ae37f730000");
    spyUserService.updateUserInfo("0520b1d9-9806-4ec1-a095-dfcd8bea8fd6", userInfoDTO);
    }
    
    
    }
  • 相关阅读:
    深入理解决策树算法
    【机器学习】一文读懂分类算法常用评价指标
    Git常用操作指南
    深度学习工作站攒机指南
    一文看懂Transformer内部原理(含PyTorch实现)
    【中文版 | 论文原文】BERT:语言理解的深度双向变换器预训练
    机器学习数学基础总结
    平均精度均值(mAP)——目标检测模型性能统计量
    【Java面试宝典】深入理解JAVA虚拟机
    Faster R-CNN:详解目标检测的实现过程
  • 原文地址:https://www.cnblogs.com/oldboyooxx/p/11147474.html
Copyright © 2020-2023  润新知