简介
Mockito 是一个模拟测试框架。主要功能是模拟类/对象的行为。
Mockito 一般用于控制调用外部的返回值,让我们只关心和测试自己的业务逻辑。
代码语言:javascript复制<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.11.2</version>
<scope>test</scope>
</dependency>
mock 方法模拟类和接口
org.mockito.Mockito
的 mock 方法可以模拟类和接口。
mock 类
代码语言:javascript复制import org.junit.Assert;
import org.junit.Test;
import java.util.Random;
import static org.mockito.Mockito.*;
public class MockitoDemo {
@Test
public void test() {
Random mockRandom = mock(Random.class);
when(mockRandom.nextInt()).thenReturn(100); // 指定调用 nextInt 方法时,永远返回 100
Assert.assertEquals(100, mockRandom.nextInt());
}
}
注意,mock 对象的方法的返回值默认都是返回类型的默认值
。例如,返回类型是 int,默认返回值是 0;返回类型是一个类,默认返回值是 null。
import org.junit.Assert;
import org.junit.Test;
import java.util.Random;
import static org.mockito.Mockito.*;
public class MockitoDemo {
@Test
public void test() {
Random mockRandom = mock(Random.class);
System.out.println mockRandom.nextBoolean());
System.out.println(mockRandom.nextInt());
System.out.println(mockRandom.nextDouble());
}
}
返回结果:
false
0
0.0
mock 接口
代码语言:javascript复制@Test
public void test3() {
List mockList = mock(List.class);
Assert.assertEquals(0, mockList.size());
Assert.assertEquals(null, mockList.get(0));
// 调用 mock 对象的写方法,是没有效果的
mockList.add("a");
// 没有指定 size() 方法返回值,这里结果是默认值
Assert.assertEquals(0, mockList.size());
// 没有指定 get(0) 返回值,这里结果是默认值
Assert.assertEquals(null, mockList.get(0));
// 指定 get(0)时返回 a
when(mockList.get(0)).thenReturn("a");
// 没有指定 size() 方法返回值,这里结果是默认值
Assert.assertEquals(0, mockList.size());
// 因为上面指定了 get(0) 返回 a,所以这里会返回 a
Assert.assertEquals("a", mockList.get(0));
// 没有指定 get(1) 返回值,这里结果是默认值
Assert.assertEquals(null, mockList.get(1));
}
@Mock 注解
@Mock 注解可以理解为对 mock 方法的一个替代。使用该注解时,要使用MockitoAnnotations.initMocks
方法,让注解生效。
MockitoAnnotations.initMocks
放在 junit 的@Before注解修饰的函数中更合适。
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Random;
import static org.mockito.Mockito.*;
public class MockitoDemo {
@Mock
private Random random;
@Before
public void before() {
// 让注解生效
MockitoAnnotations.initMocks(this);
}
@Test
public void test() {
when(random.nextInt()).thenReturn(100);
Assert.assertEquals(100, random.nextInt());
}
}
MockitoAnnotations.initMocks
的一个替代方案是使用 MockitoJUnitRunner
。
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Random;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class MockitoDemo {
@Mock
private Random random;
@Test
public void test() {
when(random.nextInt()).thenReturn(100);
Assert.assertEquals(100, random.nextInt());
}
}
参数匹配
精确匹配
代码语言:javascript复制public class MockitoTest2 {
@Mock
private List<String> mockStringList;
@Before
public void before() {
MockitoAnnotations.initMocks(this);
}
@Test
public void test() {
mockStringList.add("a");
when(mockStringList.get(0)).thenReturn("a");
when(mockStringList.get(1)).thenReturn("b");
Assert.assertEquals("a", mockStringList.get(0));
Assert.assertEquals("b", mockStringList.get(1));
}
@Test
public void test2() {
mockStringList.add("a");
// 虽然可以用eq进行精确匹配,但是有点多余
when(mockStringList.get(eq(0))).thenReturn("a");
when(mockStringList.get(eq(1))).thenReturn("b");
Assert.assertEquals("a", mockStringList.get(0));
Assert.assertEquals("b", mockStringList.get(1));
}
}
模糊匹配
可以使用Mockito.anyInt()
匹配所有类型为 int 的参数:
public class MockitoTest2 {
@Mock
private List<String> mockStringList;
@Before
public void before() {
MockitoAnnotations.initMocks(this);
}
@Test
public void test1() {
mockStringList.add("a");
// 使用 Mockito.anyInt() 匹配所有的 int
when(mockStringList.get(anyInt())).thenReturn("a");
Assert.assertEquals("a", mockStringList.get(0));
Assert.assertEquals("a", mockStringList.get(1));
}
}
anyInt 只是用来匹配参数的工具之一,目前 mockito 有多种匹配函数,部分如下:
函数名 | 匹配类型 |
---|---|
any() | 所有对象类型 |
anyInt() | 基本类型 int、非 null 的 Integer 类型 |
anyChar() | 基本类型 char、非 null 的 Character 类型 |
anyShort() | 基本类型 short、非 null 的 Short 类型 |
anyBoolean() | 基本类型 boolean、非 null 的 Boolean 类型 |
anyDouble() | 基本类型 double、非 null 的 Double 类型 |
anyFloat() | 基本类型 float、非 null 的 Float 类型 |
anyLong() | 基本类型 long、非 null 的 Long 类型 |
anyByte() | 基本类型 byte、非 null 的 Byte 类型 |
anyString() | String 类型(不能是 null) |
anyList() | List<T> 类型(不能是 null) |
anyMap() | Map<K, V>类型(不能是 null) |
anyCollection() | Collection<T>类型(不能是 null) |
anySet() | Set<T>类型(不能是 null) |
any(Class<T> type) | type类型的对象(不能是 null) |
isNull() | null |
notNull() | 非 null |
isNotNull() | 非 null |
参数匹配顺序
如果参数匹配即声明了精确匹配,也声明了模糊匹配;又或者同一个值的精确匹配出现了两次,使用时会匹配哪一个?会匹配符合匹配条件的最新声明的匹配。
代码语言:javascript复制import org.junit.Assert;
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.*;
@RunWith(MockitoJUnitRunner.class)
public class MockitoDemo {
@Mock
private List<String> testList;
@Test
public void test01() {
// 精确匹配 0
when(testList.get(0)).thenReturn("a");
Assert.assertEquals("a", testList.get(0));
// 精确匹配 0
when(testList.get(0)).thenReturn("b");
Assert.assertEquals("b", testList.get(0));
// 模糊匹配
when(testList.get(anyInt())).thenReturn("c");
Assert.assertEquals("c", testList.get(0));
Assert.assertEquals("c", testList.get(1));
}
@Test
public void test02() {
// 模糊匹配
when(testList.get(anyInt())).thenReturn("c");
Assert.assertEquals("c", testList.get(0));
Assert.assertEquals("c", testList.get(1));
// 精确匹配 0
when(testList.get(0)).thenReturn("a");
Assert.assertEquals("a", testList.get(0));
Assert.assertEquals("c", testList.get(1));
}
}
@Spy 注解
spy 和 mock不同,不同点是:
- spy 的参数是对象示例,mock 的参数是 class
- 被 spy 的对象,调用其方法时默认会走真实方法,mock 对象不会
public class MockitoTest4 {
// 测试 spy
@Test
public void test_spy() {
ExampleService spyExampleService = spy(new ExampleService());
// 默认会走真实方法
Assert.assertEquals(3, spyExampleService.add(1, 2));
// 打桩后,不会走了
when(spyExampleService.add(1, 2)).thenReturn(10);
Assert.assertEquals(10, spyExampleService.add(1, 2));
}
// 测试 mock
@Test
public void test_mock() {
ExampleService mockExampleService = mock(ExampleService.class);
// 默认返回结果是返回类型int的默认值
Assert.assertEquals(0, mockExampleService.add(1, 2));
}
}
class ExampleService {
int add(int a, int b) {
System.out.println("add 方法");
return a b;
}
}
spy对应注解 @Spy
和 @Mock
是一样用的。
public class MockitoTest5 {
@Spy
private ExampleService5 spyExampleService;
@Test
public void test_spy() {
MockitoAnnotations.initMocks(this);
Assert.assertEquals(3, spyExampleService.add(1, 2));
when(spyExampleService.add(1, 2)).thenReturn(10);
Assert.assertEquals(10, spyExampleService.add(1, 2));
}
}
class ExampleService5 {
int add(int a, int b) {
return a b;
}
}
对于@Spy
,如果发现修饰的变量是 null,会自动调用类的无参构造函数来初始化。所以下面两种写法是等价的:
// 写法1
@Spy
private ExampleService spyExampleService;
// 写法2
@Spy
private ExampleService spyExampleService = new ExampleService();
如果没有无参构造函数,必须使用写法2,例子:
代码语言:javascript复制import org.junit.Assert;
import org.junit.Test;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
class ExampleService {
private int a;
public ExampleService(int a) {
this.a = a;
}
int add(int b) {
return a b;
}
}
public class MockitoDemo {
@Spy
private ExampleService spyExampleService = new ExampleService(1);
@Test
public void test_spy() {
MockitoAnnotations.initMocks(this);
Assert.assertEquals(3, spyExampleService.add(2));
}
}
@InjectMocks 注解注入mock对象
mockito 会将 @Mock
、@Spy
修饰的对象自动注入到 @InjectMocks
修饰的对象中。
注入方式有多种,mockito 会按照下面的顺序尝试注入:
- 构造函数注入
- 设值函数注入(set函数)
- 属性注入
import java.util.Random;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import static org.mockito.Mockito.when;
public class MockitoTest6 {
@Mock
private HttpService6 httpService;
@InjectMocks
private ExampleService6 exampleService = new ExampleService6(); // 会将 httpService 注入进去
@Test
public void test01() {
MockitoAnnotations.initMocks(this);
when(httpService.queryStatus()).thenReturn(0);
Assert.assertEquals("你好", exampleService.hello());
}
}
class HttpService6 {
public int queryStatus() {
// 发起网络请求,提取返回结果
// 这里用随机数模拟结果
return new Random().nextInt(2);
}
}
class ExampleService6 {
private HttpService6 httpService;
public String hello() {
int status = httpService.queryStatus();
if (status == 0) {
return "你好";
}
else if (status == 1) {
return "Hello";
}
else {
return "未知状态";
}
}
}
https://www.letianbiji.com/java-mockito/mockito-mock.html