引入依赖
- SpringBoot项目
spring-boot-starter-test依赖中自动引入了mockito的依赖,所以无需额外引入依赖。
- 一般项目
单独引入mockito依赖
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>4.5.1</version>
<scope>test</scope>
</dependency>
使用示例
mock对象的创建与使用
// 静态导入会使代码更简洁
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
// 使用mock方法创建一个List类型的mock对象
List mockList = mock(List.class);
// 调用mock对象的add方法
boolean res = mockList.add("a");
System.out.println("res = " + res);// res = false
// 调用mock对象的get方法
Object ret = mockList.get(3);
System.out.println("ret = " + ret);// ret = null
// 通过verify校验是否执行过mock对象的指定方法
verify(mockList).add("a");
//verify(mockList).add("b"); //由于未执行过,会抛出异常
verify(mockList).get(3);
-
既可以mock接口类,也可以mock实现类。
-
mock对象的方法具有默认的返回值,会适当的返回null,原始类型,原始类型的包装类,一个空的集合,例如int/Integer返回0、boolean/Boolean返回false;
-
mock对象会记住所有的交互动作,你可以使用verify来验证是否执行过。
mock方法的执行-打桩stubbing
// 你可以mock具体的类,不仅只是接口
LinkedList mockList = mock(LinkedList.class);
// mock方法的返回值,即打桩stubbing
when(mockList.get(0)).thenReturn("first");
when(mockList.get(1)).thenThrow(new RuntimeException());
// 输出“first”
System.out.println(mockList.get(0));
// 抛出异常
//System.out.println(mockList.get(1));
// 因为get(999) 没有打桩,因此输出null
System.out.println(mockList.get(999));
- 对mock对象的某个函数调用打桩后,该mock对象的该函数调用将会一直返回固定的值;
- 打桩动作可以被覆写 : 例如常见的打桩动作可以作为公共方法,然后在不同测试方法中能够重新打桩。
- 除了thenReturn和thenThrow,还可以调用thenCallRealMethod,thenAnswer等方法。
对无返回值方法打桩
在when方法无法调用无返回值方法,所以对于无返回值方法,需要采用下面的方式进行打桩:
doThrow(new RuntimeException()).when(mockedList).clear();
// 下面的代码会抛出运行时异常
mockedList.clear();
除了doThrow,你还可以使用下面一些方法:
使用内置参数匹配器
Mockito提供了许多内置的参数匹配器,使验证和打桩变得更灵活。
LinkedList mockList = mock(LinkedList.class);
// anyInt()匹配器
when(mockList.get(anyInt())).thenReturn("element");
System.out.println(mockList.get(0));
// any(Class<T> type)匹配器
when(mockList.contains(any(String.class))).thenReturn(true);
System.out.println(mockList.contains(10));
System.out.println(mockList.contains("10"));
常用的内置匹配器有(详细的参数匹配器介绍参考官方文档):
- any():匹配任何东西,包括空值和可变参数。
- any(Class type):匹配给定类型的任何对象,不包括空值。
- anyXxx()
- contains(String substring):包含给定子字符串的字符串参数。
- startsWith(String prefix) / endsWith(String suffix):以给定前缀/后缀结束的字符串参数。
- eq(Xxx value):等值匹配。
- isA(Class type):对象参数,该参数实现给定的类。
- isNotNull() / isNull()
- same(T value):与给定值相同的对象参数。
需要注意的是,如果一个方法参数使用了匹配器,则其他方法参数也必须使用匹配器,例如:
Map mockMap = mock(Map.class);
//when(mockMap.put(anyInt(), "aaa")).thenReturn("ele");// 使用原始类型,会抛出异常
when(mockMap.put(anyInt(), eq("aaa"))).thenReturn("ele");// 使用eq匹配器,正常
System.out.println(mockMap.put(1, "aaa"));
使用自定义参数匹配器
有时候我们需要使用自定义的参数匹配器,一般来说,我们要实现ArgumentMatcher接口,示例如下:
class ListOfTwoElements implements ArgumentMatcher<List> {
public boolean matches(List list) {
return list.size() == 2;
}
public String toString() {
// verify校验失败时打印
return "[list of 2 elements]";
}
}
List mock = mock(List.class);
when(mock.addAll(argThat(new ListOfTwoElements))).thenReturn(true);
mock.addAll(Arrays.asList("one", "two"));
verify(mock).addAll(argThat(new ListOfTwoElements()));
为了保持它的可读性,可以提取方法,例如:
verify(mock).addAll(argThat(new ListOfTwoElements()));
// 变成
verify(mock).addAll(listOfTwoElements());
也可以使用lambda表达式:
verify(mock).addAll(argThat(list -> list.size() == 2));
此外,还提供了一些原始类型的自定义参数匹配器,如下:
-
booleanThat(Matcher matcher)
-
byteThat(Matcher matcher)
-
charThat(Matcher matcher)
-
doubleThat(Matcher matcher)
-
floatThat(Matcher matcher)
-
intThat(Matcher matcher)
-
longThat(Matcher matcher)
-
shortThat(Matcher matcher)
mock对象的三种方式
一、mock()方法
在前面已经介绍了
二、@mock
- 在JUnit4中使用
使用测试基类方式:
注意:@Test、@Before、@After都为JUnit4中的
import org.junit.After;
import org.junit.Before;
import org.mockito.MockitoAnnotations;
// 测试基类
public class SampleBaseTestCase {
private AutoCloseable closeable;
@Before
public void openMocks() {
closeable = MockitoAnnotations.openMocks(this);
}
@After
public void releaseMocks() throws Exception {
closeable.close();
}
}
import org.junit.Test;
import org.mockito.Mock;
import java.util.ArrayList;
import static org.mockito.Mockito.when;
// 测试类
public class TestDemo2 extends SampleBaseTestCase{
@Mock
private ArrayList mockList;
@Test
public void test1() {
when(mockList.get(1)).thenReturn("aaa");
System.out.println(mockList.get(1));
}
}
使用注解方式:在测试类上加@RunWith(MockitoJUnitRunner.class)注解
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.ArrayList;
import static org.mockito.Mockito.when;
// 测试类
@RunWith(MockitoJUnitRunner.class)
public class TestDemo2{
@Mock
private ArrayList mockList;
@Test
public void test1() {
when(mockList.get(1)).thenReturn("aaa");
System.out.println(mockList.get(1));
}
}
- 在JUnit5中使用
使用测试基类方式:
注意:@Test、@BeforeEach、@AfterEach都为JUnit4中的
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.mockito.MockitoAnnotations;
// 测试基类
public class SampleBaseTestCase {
private AutoCloseable closeable;
@BeforeEach
public void openMocks() {
closeable = MockitoAnnotations.openMocks(this);
}
@AfterEach
public void releaseMocks() throws Exception {
closeable.close();
}
}
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import java.util.ArrayList;
import static org.mockito.Mockito.when;
// 测试类
public class TestDemo2 extends SampleBaseTestCase{
@Mock
private ArrayList mockList;
@Test
public void test1() {
when(mockList.get(1)).thenReturn("aaa");
System.out.println(mockList.get(1));
}
}
使用注解方式:在测试类上加@ExtendWith(MockitoExtension.class)注解
import org.junit.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.ArrayList;
import static org.mockito.Mockito.when;
// 测试类
@ExtendWith(MockitoExtension.class)
public class TestDemo2{
@Mock
private ArrayList mockList;
@Test
public void test1() {
when(mockList.get(1)).thenReturn("aaa");
System.out.println(mockList.get(1));
}
}
- 配合@InjectMocks注解可以实现依赖注入
@ExtendWith(MockitoExtension.class)
public class InjectTest {
@Mock
private UserMapper mockMapper;
// mockMapper会自动注入到userService中
@InjectMocks
private final UserService userService = new UserService();
@Test
public void test1() {
when(mockMapper.count()).thenReturn(99);
Integer count = userService.countUser();
System.out.println("count = " + count);// count=99
}
}
三、@MockBean【Spring环境】
在SpringBoot中,会自动将@MockBean注解生成的Mock对象注入到合适的Bean中
@SpringBootTest
class UserServiceTest {
@MockBean
private UserMapper mockMapper;
// mockMapper会自动注入到userService中
@Autowired
private UserService userService;
@Test
public void userCountTest() {
when(mockMapper.count()).thenReturn(99);
Integer count = userService.countUser();
System.out.println("count = " + count);
}
}
spy对象的三种方式
有时候需要在真实对象上mock方法,使用spy可以创建或者放入一个真实对象进行Stubbing,用spy创建的对象都是真实对象。类似于创建mock对象,spy创建对象也有三种方式。
一、spy()方法
List list = new LinkedList();
List spy = spy(list);
二、@spy注解
配合@InjectMocks注解可以实现依赖注入
@Spy
UserMapper userMapper;
@InjectMocks
UserService userService = new UserService();
三、@SpyBean【Spring环境】
@SpyBean
UserMapper userMapper;
编写BDD风格的测试方法
BDD,即行为驱动开发(Behavior-Driven Development)。用行为驱动开发的风格来写测试用例,会使用//given //when //then 作为测试方法的基础组成部分。
在Mockito中,使用提供的 BDDMockito.given(Object) 方法可以很好的应用到BDD风格的测试代码中,测试用例看起来会是这样子:
import static org.mockito.BDDMockito.*;
Seller seller = mock(Seller.class);
Shop shop = new Shop(seller);
public void shouldBuyBread() throws Exception {
//given
given(seller.askForBread()).willReturn(new Bread());
//when
Goods goods = shop.buyBread();
//then
assertThat(goods, containBread());
}
参考资料: