Mockito 基础
Mockito 是实现mock技术的java框架。
mock技术主要解决非常难创建的对象进行模拟,或者是在测试阶段无法创建的对象进行模拟。
常见的模拟如:RPC远程调用,数据库,Minio,Kafka等
相关资源
框架官网网站:
本文只讲基本使用,不会设计到所有Mockito的命令,具体其他命令参考官网Document
基本使用
1.通过静态导入来让mokito命令写的简洁
(后面的所有讲述内容都以简写来描述)
在import的地方加入:
import static org.mockito.Mockito.*;
加入之后mockito语法的格式:
doReturn("Hello").when(someClass).sayHello()
没加入上面的import语句的格式:
Mockito.doReturn("Hello").when(someClass).sayHello()
2.基本命令mock和spy
2.1 mock和spy的区别:
mock: 封装一个空壳的对象,如果没有使用mock指定返回值,则直接返回null
spy: 封装真实对象,可以选择是否执行原始功能,没有指定的mock函数会按照正常流程走
2.2 基本使用语法
2.2.1 mock使用例子:
// 新建一个空壳对象(任何方法都不会被真实执行)
TestClass mock = mock(TestClass.class);
// 例如 我需要当mock对象执行Print方法的时候返回10
// 写法1
when(mock.Print()).thenReturn(10);
// 写法2
doReturn(10).when(mock).Print();
// 执行测试
System.out.println(mock.Print());
2.2.2 spy使用例子:
// 创建一个真实对象,因为spy会保留真实对象功能
TestClass testClass = new TestClass();
// 使用spy创建一个testClass的间谍对象
TestClass spy = spy(testClass);
// 不会执行原始功能写法
doReturn(10).when(spy).Print();
// 会执行原始功能的写法
when(spy.Print()).thenReturn(10);
// 测试运行
System.out.println(spy.Print());
3. mockito在Spring Boot中的使用
Spring Boot中使用Mokito有2个注解可以方便的创建Mock对象和Spy对象, 并将其注入到Spring容器,如果是Single的对象则会替换。 (个人不推荐使用,本人在测试的时候遇到非常多的bug问题。后面会讲到我的解决方法和推荐的方法)
// 这一个注解等同于mock(TestClass.class) 然后再将mock对象注入到Spring容器
@MockBean
private TestClass testClass;
// 这一个注解等同于spy(testClass) 首先从容器中拿到TestClass的对象然后对其进行spy然后注入到Spring容器
@SpyBean
private TestClass testClass;
关于SpyBean的使用案例
UserDao.java代码
@Component
public class UserDao {
public void printUser(){
System.out.println("打印一个User");
}
}
UserService.java代码
@Service
public class UserService {
@Resource
private UserDao userDao;
public void printUser(){
userDao.printUser();
}
}
测试代码:
import static org.mockito.Mockito.*;
@SpringBootTest(classes = {MainApplication.class})
public class UserServiceTest {
// 创建一个空壳对象并注入到容器
@MockBean
private UserDao userDao;
@Resource
private UserService userService;
@BeforeEach
void before(){
// 我需要让userDao什么都不执行, 即不会输出内容
doNothing().when(userDao).printUser();
}
@Test
void printUserTest(){
userService.printUser();
}
}
测试结果不会输出 打印一个User
4.使用Spring Test 框架+ mockito原生来测试 (本人推荐)
使用例子代码:
UserDao.java代码
@Component
public class UserDao {
public void printUser(){
System.out.println("打印一个User");
}
}
UserService.java代码
@Service
public class UserService {
@Resource
private UserDao userDao;
public void printUser(){
userDao.printUser();
}
}
测试代码:
import static org.mockito.Mockito.*;
@SpringBootTest(classes = {MainApplication.class})
public class UserServiceTest2 {
@Resource
private UserService userService;
@BeforeEach
void before() {
// 创建空壳对象
UserDao userDaoMock = mock(UserDao.class);
// 我需要让userDao什么都不执行, 即不会输出内容
doNothing().when(userDaoMock).printUser();
// 讲userService内部变量userDao指向这个新建的mock对象
ReflectionTestUtils.setField(userService, "userDao", userDaoMock);
}
@Test
void printUserTest() {
userService.printUser();
}
}
测试结果不会输出 打印一个User
5.本人遇到的坑
5.1 使用@SpyBean包裹Service对象时候,如果Service里面有@Transaction注解则会抛出异常
解决方法:
使用我上面说的 使用Spring Test 框架+ mockito原生来测试
同时不使用以下来包裹
spy(service)
而使用
mock(service.class, AdditionalAnswers.delegatesTo(具体Service实例))
5.2 使用时应要注意Spring Context Cache 的问题
在同一个Test测试文件中,对容器内容Bean的所有修改会影响本测试文件的其他测试用例。
@Resource
private IUserService userService;
@Test
void selectTest() {
// 创建Mock对象
UserDao userDaoMock = mock(UserDao.class);
doReturn(new ArraryList<User>()).when(userDaoMock).selectAllUser();
// 使用反射注入到userService实例类
ReflectionTestUtils.setField(userService,"userDao",userDaoMock);
// 调用
userService.SelectAllUser();
}
@Test
void selectTest2(){
userService.SelectAllUser();
}
注:第一个测试用例采用mock来mock指定对象,但是第二个测试用例没有mock
然后我们使用Idea运行测试用例,如果每个都单独运行,是不会有影响的。
但是如果运行整个文件的测试用例,会先运行selectTest测试用例,然后运行selectTest2测试用例。这会导致selectTest2也有selectTest的mock设置。
如果不希望有这个问题则需要按如下写:
@Resource
private IUserService userService;
@Test
@DirtiesContext // 新增注解,用于标记此测试会影响容器内容,需要运行完成后处理缓存
void selectTest() {
// 创建Mock对象
UserDao userDaoMock = mock(UserDao.class);
doReturn(new ArraryList<User>()).when(userDaoMock).selectAllUser();
// 使用反射注入到userService实例类
ReflectionTestUtils.setField(userService,"userDao",userDaoMock);
// 调用
userService.SelectAllUser();
}
@Test
void selectTest2(){
userService.SelectAllUser();
}
使用@DirtiesContext
可以清除spring 容器缓存