0


Mockito单元测试基本使用

文章目录

本文参考:

【码农教程】手把手教你Mockito的使用 - 掘金 (juejin.cn)

java - doReturn().when()与when().thenReturn() - 成长之路 - SegmentFault 思否

单元测试实践篇:Mock_阿里巴巴淘系技术团队官网博客的博客-CSDN博客

阿里是如何进行单元测试培训的?_Hollis Chuang的博客-CSDN博客

【Mockito】Mockito + Junit 5 快速入门_哔哩哔哩_bilibili

1.为什么需要Mock

测试驱动的开发( TDD)要求我们先写单元测试,再写实现代码。在写单元测试的过程中,我们往往会遇到要测试的类有很多依赖,这些依赖的类/对象/资源又有别的依赖,从而形成一个大的依赖树,要在单元测试的环境中完整地构建这样的依赖,是一件很困难的事情。如下图所示:
在这里插入图片描述
为了测试类A,我们需要Mock B类和C类(用虚拟对象来代替)如下图所示:
在这里插入图片描述

2.Mockito 中常用方法

先添加maven依赖:

mockito和junit:

<?xml version="1.0" encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.jxz</groupId><artifactId>MockitoLearning</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>org.mockito</groupId><artifactId>mockito-core</artifactId><version>4.3.1</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version></dependency></dependencies></project>

2.1 Mock 方法

mock 方法来自

org.mockito.Mock

,它表示可以 mock 一个对象或者是接口。

publicstatic<T>Tmock(Class<T> classToMock)
  • classToMock:待 mock 对象的 class 类。
  • 返回 mock 出来的类

实例:使用 mock 方法 mock 一个List类

List mockList =Mockito.mock(List.class);

也可以使用注解来快速模拟

  1. @Mock+MockitoAnnotations.openMocks(this)

也有用@Mock+MockitoAnnotations.initMocks(this)的

packagecom.jxz;importorg.junit.Before;importorg.junit.Test;importorg.mockito.Mock;importorg.mockito.MockitoAnnotations;importjava.util.List;importstaticorg.mockito.Mockito.verify;/**
 * @Author jiangxuzhao
 * @Description
 * @Date 2023/6/19
 */publicclassMockExample1{@MockprivateList mockList;@Beforepublicvoidsetup(){MockitoAnnotations.openMocks(this);}@TestpublicvoidtestMock(){
        mockList.add(1);verify(mockList).add(1);}}
  1. @Mock+@RunWith(MockitoJUnitRunner.class)
packagecom.jxz;importorg.junit.Test;importorg.junit.runner.RunWith;importorg.mockito.Mock;importorg.mockito.junit.MockitoJUnitRunner;importjava.util.List;importstaticorg.mockito.Mockito.verify;/**
 * @Author jiangxuzhao
 * @Description
 * @Date 2023/6/19
 */@RunWith(MockitoJUnitRunner.class)publicclassMockExample2{@MockprivateList mockList;@TestpublicvoidtestMock(){
        mockList.add(1);verify(mockList).add(1);}}

2.2 对 Mock 出来的对象进行行为验证和Junit结果断言

一旦mock对象被创建了,Mock对象会记录我们调用的所有交互,也就是各种方法和参数,验证的意思是”查看我们到底有没有调用过mock的这个方法“,Mockito 中验证的方法是:verify

packagecom.jxz;importorg.junit.Test;importorg.mockito.Mockito;importjava.util.List;importstaticorg.mockito.Mockito.verify;/**
 * @Author jiangxuzhao
 * @Description
 * @Date 2023/6/19
 */publicclassDemo{@TestpublicvoidtestMock(){List mockList =Mockito.mock(List.class);
        mockList.add(1);
        mockList.add("one");// verifyverify(mockList).add(1);//        verify(mockList).add("two"); //会抛出异常}}

JUnit断言使用到的类是 Assert.

@TestpublicvoidtestMock2(){List mockList =Mockito.mock(List.class);
    mockList.add(1);Assert.assertEquals(1,mockList.get(1));}

输出:

java.lang.AssertionError: 
Expected :1
Actual   :null

当使用 mock 对象时(这里是mockList),如果不对其行为进行定义(也就是下面的打桩),则 mock 对象方法的返回值为返回类型的默认值(这里为null)。

2.3 测试桩stub

指定mock对象的交互行为逻辑,基本格式

when().thenReturn()

when(mockedList.get(0)).thenReturn(“first”)规定当调用get(0)的时候返回"first"

@TestpublicvoidtestStub(){LinkedList mockList =Mockito.mock(LinkedList.class);// stubbingwhen(mockList.get(0)).thenReturn("first");when(mockList.get(1)).thenThrow(newRuntimeException());System.out.println(mockList.get(0));//firstSystem.out.println(mockList.get(1));//RuntimeException}

当我们连续两次为同一个方法使用stub的时候,他只会只用最新的一次。一旦这个方法被stub了,就会一直返回这个stub的值。如下:

@TestpublicvoidtestStub2(){LinkedList mockList =Mockito.mock(LinkedList.class);when(mockList.get(0)).thenReturn("first");when(mockList.get(0)).thenReturn("second");System.out.println(mockList.get(0));System.out.println(mockList.get(0));}

输出:

second
second

还有一种测试桩指定的方式,即

doReturn().when()

,本质上也是规定行为。

@TestpublicvoidtestDoReturn(){A a =newA();A mockA =Mockito.mock(A.class);Mockito.when(mockA.add(1,2)).thenReturn(5);// 当mockA调用add(1,2),返回5System.out.println(mockA.add(1,2));// 5A A2 =newA();A mockA2 =Mockito.mock(A.class);Mockito.doReturn(5).when(mockA2).add(1,2);// 同样是当mockA调用add(1,2),返回5System.out.println(mockA2.add(1,2));// 5}

两者本质上就是一个由于执行顺序产生的问题,

Mockito.when(mockA.add(1,2)).thenReturn(5)

,会先执行

a + b

,即

1 + 2 

, 结果本应是3,但由于后面的

thenReturn

,所以调用该方法时,实际的返回值是5。而

Mockito.doReturn(5).when(mockA2).add(1,2)

就不会执行

a+b

的操作,而会直接返回5。区别就是若是一定会执行add()方法,难免会产生无法预估的副作用,比如抛出异常等。

2.4 参数匹配器

参数匹配器可以进行参数的灵活指派。

@TestpublicvoidtestMatch(){Map mockMap =Mockito.mock(Map.class);// 正常打桩测试when(mockMap.get("key1")).thenReturn("value1");System.out.println(mockMap.get("key1"));//value1// 任意String的参数匹配器when(mockMap.get(anyString())).thenReturn("value2");System.out.println(mockMap.get(anyString()));// value2System.out.println(mockMap.get("key2"));// value2System.out.println(mockMap.get(1));// null// 多个入参时,要么都使用参数匹配器,要么都不使用,否则会异常, put()返回参数when(mockMap.put(anyString(),anyInt())).thenReturn("value3");System.out.println(mockMap.put("key3",3));// value3System.out.println(mockMap.put(anyString(),anyInt()));// value3//        System.out.println("key3",anyInt()); // 异常// verify也支持参数匹配verify(mockMap,atLeastOnce()).get(anyString());// 前面交互至少调用了一次get(anyString())verify(mockMap).put(anyString(),eq(3));// 前面至少调用了一次put(anyString(),3)}

2.5 mock()与spy()

  1. 被 spy 的对象会走真实的方法,而 mock 对象走虚拟对象的方法,返回默认值
  2. spy() 方法的参数是对象实例,mock()方法 的参数是 class

示例:

packagecom.jxz;importorg.junit.Test;importorg.mockito.Mockito;/**
 * @Author jiangxuzhao
 * @Description
 * @Date 2023/6/20
 */publicclassSpyAndMock{@TestpublicvoidtestSpy(){A a =newA();A a1 =Mockito.mock(A.class);A a2 =Mockito.spy(a);System.out.println(a1.add(1,2));System.out.println(a2.add(1,2));}}classA{publicintadd(int a,int b){return a+b;}}

输出:

03

通过mock生成的对象,会拥有以前的对象的所有方法,但是方法中都没有了功能,就比如上面的

a1

对应的类可以理解下面这样

A1 extend A{  
    pubic intadd(int a,int b){return0;}}

2.6 @InjectMocks

@InjectMocks和@Mock配合可以简化某个类中,注入类的配置。同时可以在外面对被@Mock的类进行行为指定,从而让其被调用时,产生我们指定的结果。

示例:

要测试的类和方法:

packagecom.jxz;/**
 * @Author jiangxuzhao
 * @Description
 * @Date 2023/6/20
 */publicclassRegistrationImpl{SalesDao salesDao =newSalesDao();SaveDao saveDao =newSaveDao();publicStringregister(int id,String name){String result1 = salesDao.findRep(name);System.out.println(result1);String result2 = saveDao.save(id, name);System.out.println(result2);return result1 +"_"+ result2;}}classSalesDao{publicStringfindRep(String name){return name;}}classSaveDao{publicStringsave(int id,String name){return id + name;}}

对应的测试类和方法:

packagecom.jxz;importorg.junit.Assert;importorg.junit.Test;importorg.junit.runner.RunWith;importorg.mockito.InjectMocks;importorg.mockito.Mock;importorg.mockito.junit.MockitoJUnitRunner;importstaticorg.mockito.Mockito.when;/**
 * @Author jiangxuzhao
 * @Description
 * @Date 2023/6/20
 */@RunWith(MockitoJUnitRunner.class)publicclassRegistrationImplTest{@InjectMocksprivateRegistrationImpl registrationImpl;// 需要被注入mock对象的对象@MockprivateSalesDao salesDao;// RegistrationImpl中注入的类@MockprivateSaveDao saveDao;@TestpublicvoidtestRegister(){// 进行mock注入类的行为指定// 可以看到register方法中调用mock对象打印出来的东西正确when(salesDao.findRep("jiangxuzhao")).thenReturn("jiangxuzhao666");// jiangxuzhao666when(saveDao.save(123,"jiangxuzhao")).thenReturn("123jiangxuzhao666");// 123jiangxuzhao666String result = registrationImpl.register(123,"jiangxuzhao");Assert.assertEquals(result,"jiangxuzhao666_123jiangxuzhao666");// 结合mock对象的返回值正确//        Assert.assertEquals(result,"null"); // org.junit.ComparisonFailure}}
标签: 单元测试 junit java

本文转载自: https://blog.csdn.net/qq_44036439/article/details/131299364
版权归原作者 蒋大钊! 所有, 如有侵权,请联系我们删除。

“Mockito单元测试基本使用”的评论:

还没有评论