栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

使用 mockito 进行单元测试

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

使用 mockito 进行单元测试

博客主页:JavaProfessional
一个专注于Java的博主,致力于使用通俗易懂的语句讲解内容,期待您的关注!

文章目录
  • 前言
  • 依赖
  • 基本操作
    • 模拟任意类或接口
    • 返回模拟的数据——插桩stubbing
    • 验证
      • 验证某一方法是否执行
      • 验证某一个方法执行次数
      • 验证执行顺序
      • 验证类的所有模拟方法是否都被验证了
    • 参数匹配
    • 模拟异常
  • 参考资料
  • 好文推荐

前言

当前Spring是Java的一大杀器,但是因为所有依赖关系都已经被Spring解决掉了,就导致我们在写完Spring程序后想写单元测试却无从下手。而且现在ORM框架为我们解决了数据库问题,导致我们的程序离开了数据库之后完全不能执行,想写单元测试更是难上加难。

依赖
 
基本操作 
模拟任意类或接口 

当你模拟任何类得到一个对象时,该对象将获取该类对象的所有操作。

import java.util.List;

import static org.mockito.Mockito.mock;

@Slf4j
public class AppNoticeServiceTest {
    @Test
    public void baseOperation() {
        List mockedList = mock(List.class);
        boolean addSuccess = mockedList.add(1);
        if(addSuccess) {
            // 程序略
            log.info("add执行成功");
        } else {
            // 程序略
            log.info("add执行失败");
        }
    }
}

上述我们模拟了一个List接口。

返回模拟的数据——插桩stubbing

思考一下,上一个例子当中,我可以把mockedList的第一个数据打印出来吗?按照我们之前的逻辑当然可以,我不是有一个add的操作吗?但是仔细想想,我们模拟的可是List接口啊,都没有指明实现类,难道mockito已经智能到这种程度了吗?当然不是,其实mockito对象只是具备了类的方法,但是调用方法并不会产生真实的数据,我们必须告诉mockito对象执行某种操作后,给我一个什么数据。比如上述代码中,我们执行add操作后,给我返回一个true或false,好让我控制之后的程序执行路径。

package com.bootdo.app.service.impl;

import lombok.extern.slf4j.Slf4j;
import org.junit.Test;

import java.util.List;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

@Slf4j
public class AppNoticeServiceTest {
    @Test
    public void baseOperation() {
        List mockedList = mock(List.class);
        when(mockedList.add(1)).thenReturn(Boolean.FALSE);
        boolean addSuccess = mockedList.add(1);
        if(addSuccess) {
            // 程序略
            log.info("add执行成功");
        } else {
            // 程序略
            log.info("add执行失败");
        }
    }
}

此段代码我们去除了add操作,因为add操作并不是我们真实的目的,真实目的是我们想要控制程序的执行走的路径,达到全覆盖。

验证 验证某一方法是否执行

我们如何验证程序按照我们想要的路径争取执行了呢?可以使用verify。Mockito verify方法用于检查是否发生了某些行为,我们可以在测试方法代码的末尾使用Mockito验证方法,以确保调用了指定的方法。

package com.bootdo.app.service.impl;

import lombok.extern.slf4j.Slf4j;
import org.junit.Test;

import java.util.List;

import static org.mockito.Mockito.*;

@Slf4j
public class AppNoticeServiceTest {
    @Test
    public void baseOperation() {
        List mockedList = mock(List.class);
        when(mockedList.get(1)).thenReturn("one");
        String content =(String) mockedList.get(1);
        if(content.equals("one")) {
            // 程序略
            mockedList.add("two");
        } else {
            // 程序略
            mockedList.add("three");
        }
        verify(mockedList).add("two");
    }
}

我们成功验证了mockedList执行了add("two")方法,证明走了true分支。

验证某一个方法执行次数
 mockedList.add("three times");
 mockedList.add("three times");
 mockedList.add("three times");
 verify(mockedList, times(3)).add("three times");
验证执行顺序
 // A. Single mock whose methods must be invoked in a particular order
 List singleMock = mock(List.class);

 //using a single mock
 singleMock.add("was added first");
 singleMock.add("was added second");

 //create an inOrder verifier for a single mock
 InOrder inOrder = inOrder(singleMock);

 //following will make sure that add is first called with "was added first", then with "was added second"
 inOrder.verify(singleMock).add("was added first");
 inOrder.verify(singleMock).add("was added second");

两个类的执行顺序

 // B. Multiple mocks that must be used in a particular order
 List firstMock = mock(List.class);
 List secondMock = mock(List.class);

 //using mocks
 firstMock.add("was called first");
 secondMock.add("was called second");

 //create inOrder object passing any mocks that need to be verified in order
 InOrder inOrder = inOrder(firstMock, secondMock);

 //following will make sure that firstMock was called before secondMock
 inOrder.verify(firstMock).add("was called first");
 inOrder.verify(secondMock).add("was called second");
验证类的所有模拟方法是否都被验证了

这个方法很难理解,而且网上的资料大多数都是直译官网,非常难以理解。以下我们通过一个例子来理解一下:

public class Test {
    public String method1(){return "method1";}
    public String method2(){return "method2";}
}
public class MockTest {
    public static void main(String[] args) {
        Test test = mock(Test.class);
        test.method1();
        verifyNoMoreInteractions(test);
    }
}

以上调用了test.method1()但是却没有验证该结果,所以会报错。

public class MockTest {
    public static void main(String[] args) {
        Test test = mock(Test.class);
        test.method1();
        verify(test).method1();
        verifyNoMoreInteractions(test);
    }
}

以上的程序虽然仅仅掉了test的method1方法,没有调用test的method2方法,但是却在调用method1之后,进行了验证了执行次数(默认为1),所以verifyNoMoreInteractions就不会报错了

参数匹配

如果一个方法需要填写参数,但是输入什么参数并不是很重要,重要的是他的返回值,那么你可以使用参数匹配。

when(mockedList.get(anyInt())).thenReturn("element");

你甚至可以模拟空指针和任意类

when(mock.dryRun(isNull())).thenReturn("state");
when(mock.dryRun(any(String.class))).thenReturn("state");
模拟异常
doThrow(new RuntimeException()).when(mockedList).clear();
// 以下程序执行会抛出异常
mockedList.clear();
参考资料

官方文档

好文推荐
  1. 通俗易懂JVM
  2. 为什么Spring中的IOC(控制反转)能够降低耦合性(解耦)?
  3. 效率翻倍,这些idea的逆天功能你知道吗?
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/995999.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号