Mockito单元测试基本使用
本文参考:
java - doReturn().when()与when().thenReturn() - 成长之路 - SegmentFault 思否
1.为什么需要Mock
测试驱动的开发( TDD)要求我们先写单元测试,再写实现代码。在写单元测试的过程中,我们往往会遇到要测试的类有很多依赖,这些依赖的类/对象/资源又有别的依赖,从而形成一个大的依赖树,要在单元测试的环境中完整地构建这样的依赖,是一件很困难的事情。如下图所示:
为了测试类A,我们需要Mock B类和C类(用虚拟对象来代替)如下图所示:
2.Mockito 中常用方法
先添加maven依赖:
mockito和junit:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="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 一个对象或者是接口。
public static <T> T mock(Class<T> classToMock)
- classToMock:待 mock 对象的 class 类。
- 返回 mock 出来的类
实例:使用 mock 方法 mock 一个List类
List mockList = Mockito.mock(List.class);
也可以使用注解来快速模拟
- @Mock MockitoAnnotations.openMocks(this)
也有用@Mock MockitoAnnotations.initMocks(this)的
package com.jxz;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.List;
import static org.mockito.Mockito.verify;
/**
* @Author jiangxuzhao
* @Description
* @Date 2023/6/19
*/
public class MockExample1 {
@Mock
private List mockList;
@Before
public void setup(){
MockitoAnnotations.openMocks(this);
}
@Test
public void testMock(){
mockList.add(1);
verify(mockList).add(1);
}
}
- @Mock @RunWith(MockitoJUnitRunner.class)
package com.jxz;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.List;
import static org.mockito.Mockito.verify;
/**
* @Author jiangxuzhao
* @Description
* @Date 2023/6/19
*/
@RunWith(MockitoJUnitRunner.class)
public class MockExample2 {
@Mock
private List mockList;
@Test
public void testMock(){
mockList.add(1);
verify(mockList).add(1);
}
}
2.2 对 Mock 出来的对象进行行为验证和Junit结果断言
一旦mock对象被创建了,Mock对象会记录我们调用的所有交互,也就是各种方法和其调用参数,验证的意思是”查看我们到底有没有输入特定的调用参数,调用过mock的这个方法“,Mockito 中验证的方法是:verify
package com.jxz;
import org.junit.Test;
import org.mockito.Mockito;
import java.util.List;
import static org.mockito.Mockito.verify;
/**
* @Author jiangxuzhao
* @Description
* @Date 2023/6/19
*/
public class Demo {
@Test
public void testMock(){
List mockList = Mockito.mock(List.class);
mockList.add(1);
mockList.add("one");
// verify
verify(mockList).add(1);
// verify(mockList).add("two"); //会抛出异常
}
}
JUnit断言使用到的类是 Assert.
@Test
public void testMock2(){
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"
@Test
public void testStub(){
LinkedList mockList = Mockito.mock(LinkedList.class);
// stubbing
when(mockList.get(0)).thenReturn("first");
when(mockList.get(1)).thenThrow(new RuntimeException());
System.out.println(mockList.get(0)); //first
System.out.println(mockList.get(1)); //RuntimeException
}
当我们连续两次为同一个方法使用stub的时候,他只会只用最新的一次。一旦这个方法被stub了,就会一直返回这个stub的值。如下:
@Test
public void testStub2(){
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()
,本质上也是规定行为。
@Test
public void testDoReturn(){
A a = new A();
A mockA = Mockito.mock(A.class);
Mockito.when(mockA.add(1,2)).thenReturn(5); // 当mockA调用add(1,2),返回5
System.out.println(mockA.add(1,2)); // 5
A A2 = new A();
A mockA2 = Mockito.mock(A.class);
Mockito.doReturn(5).when(mockA2).add(1,2); // 同样是当mockA调用add(1,2),返回5
System.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 参数匹配器
参数匹配器可以进行参数的灵活指派。
@Test
public void testMatch(){
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())); // value2
System.out.println(mockMap.get("key2")); // value2
System.out.println(mockMap.get(1)); // null
// 多个入参时,要么都使用参数匹配器,要么都不使用,否则会异常, put()返回参数
when(mockMap.put(anyString(),anyInt())).thenReturn("value3");
System.out.println(mockMap.put("key3",3)); // value3
System.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()
- 被 spy 的对象会走真实的方法,而 mock 对象走虚拟对象的方法,返回默认值
- spy() 方法的参数是对象实例,mock()方法 的参数是 class
示例:
package com.jxz;
import org.junit.Test;
import org.mockito.Mockito;
/**
* @Author jiangxuzhao
* @Description
* @Date 2023/6/20
*/
public class SpyAndMock {
@Test
public void testSpy(){
A a = new A();
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));
}
}
class A{
public int add(int a, int b){
return a b;
}
}
输出:
0
3
通过mock生成的对象,会拥有以前的对象的所有方法,但是方法中都没有了功能,就比如上面的a1
对应的类可以理解下面这样
A1 extend A {
pubic int add(int a, int b) {
return 0;
}
}
2.6 @InjectMocks
@InjectMocks和@Mock配合可以简化某个类中,注入类的配置。同时可以在外面对被@Mock的类进行行为指定,从而让其被调用时,产生我们指定的结果。
示例:
要测试的类和方法:
package com.jxz;
/**
* @Author jiangxuzhao
* @Description
* @Date 2023/6/20
*/
public class RegistrationImpl {
SalesDao salesDao = new SalesDao();
SaveDao saveDao = new SaveDao();
public String register(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;
}
}
class SalesDao{
public String findRep(String name){
return name;
}
}
class SaveDao{
public String save(int id, String name){
return id name;
}
}
对应的测试类和方法:
package com.jxz;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import static org.mockito.Mockito.when;
/**
* @Author jiangxuzhao
* @Description
* @Date 2023/6/20
*/
@RunWith(MockitoJUnitRunner.class)
public class RegistrationImplTest {
@InjectMocks
private RegistrationImpl registrationImpl; // 需要被注入mock对象的对象
@Mock
private SalesDao salesDao; // RegistrationImpl中注入的类
@Mock
private SaveDao saveDao;
@Test
public void testRegister(){
// 进行mock注入类的行为指定
// 可以看到register方法中调用mock对象打印出来的东西正确
when(salesDao.findRep("jiangxuzhao")).thenReturn("jiangxuzhao666"); // jiangxuzhao666
when(saveDao.save(123,"jiangxuzhao")).thenReturn("123jiangxuzhao666"); // 123jiangxuzhao666
String result = registrationImpl.register(123, "jiangxuzhao");
Assert.assertEquals(result,"jiangxuzhao666_123jiangxuzhao666"); // 结合mock对象的返回值正确
// Assert.assertEquals(result,"null"); // org.junit.ComparisonFailure
}
}
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhgiheak
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01 -
怎样阻止微信小程序自动打开
PHP中文网 06-13