什么是mockito
是什么
- 是一个java语言下的一单元测试框架
使用场景
- 在资源环境不完善的情况下也可进行单元测试,确保开发进度与程序的正确性
- 在整个项目中通常依赖多个部分组成如:数据库、缓存数据库、第三方系统等等。。
- 在团队并发开发过程中,总不可能每个每个依赖资源都是准备好的,所以有没有办法缺点这些组件的情况下也可以进行部分代码的单元测试呢?这里使用mockit+junit test 可以解决
概念
mock对象
- 通过反射技术创建出来的一个代理对象;
- 这个对象可模拟真实场景去执行预期的程序与结果
打桩
- 可以简单的理解为为其设置期待值如:
when(mockLinkedList.get(0)).thenReturn("first")
// 理解:将每一个元素设置为"first" 在需要的使用每0个元素时就返回值为“first”
验证
- 验证是否有执行过、或者执行了几次;mock 对象创建后就会监控对mock对象执行的每一个操作
verify(mockLinkedList).get(0)
// 理解:验证get(0)被调用的次数
常用注解
- @Mock: 自动注入一个代理对象
- @Spy:自动注入一个真实对象
- @InjectMocks:声明为一个需要依赖其他对象,并自动将带有@Mock 的对象注入到应该对象中
创建mock对象的三种方式
版本 | 方式一 | 方式二 | 方式三 |
---|---|---|---|
Junit4 | @RunWith(MockitoJunitRunner.class) | Mockito.mock(xx.class)/Mockito.spy(xx.class) | MockitoAnnotations.openMocks(class)+@Mock注解 |
Junit5 | @ExtendWith(MockitoExtension.class) | Mockito.mock(xx.class)/Mockito.spy(xx.class) | MockitoAnnotations.openMocks(class)+@Mock注解 |
插桩的三种方式:
doReturn(返回值).when(类句柄).doXX();
使用场景:
- 对于spy 调用时并不会调用一次目标方法,而是直接返回指定值。
- 真实调用方法时,有其他依赖方法调用了数据库、缓存、消息等,如果你想跳过这些调用则可以使用此方法来跳过这些调用;
// Setup
final List<DriverBo> expectedResult =
Arrays.asList(
DriverBo.builder()
.id(0L)
.contactInformations(Arrays.asList(DriverContactInformationBo.builder().build()))
.build());
// 调用queryList方法时,直接返回指定的数据
Mockito.doReturn(Arrays.asList(Convert.toList(expectedResult)))
.when(driverServiceImplUnderTest)
.queryList(Arrays.asList(0L));
// Run the test
final List<DriverBo> result = driverServiceImplUnderTest.queryList(Arrays.asList(0L));
// Verify the results
assertThat(result.size()).isEqualTo(expectedResult.size());
when(类句柄).thenReturn(返回值);
使用场景:
- 对于spy 调用时会先调用一次目标方法。
doNothing().when(类句柄).doXX();
使用场景:
- 处理返回值为null 的方法。
插入桩抛出异常的三种方式:
- doThrow(RuntimeException.class.when(list.get(4)); 这种方式适用于没有返回值和spy 对象;
- when(list.get(4)).thenThrow(RuntimeException.class); 这种方式适用于有返回值和mock对象;
调用真实方法
// 使用mockito对象时,真实调用目标方法
Mockito.when(xxService.method(arg))
.thenCallRealMethod();
指定插桩逻辑
// 自定义插桩逻辑,invocation可以拿到对应的调用方法,mock对象,入参等信息
Mockito.doAnswer(
new Answer() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
Object argument = invocation.getArgument(0);
return 1;
}
})
.when(driverServiceImplUnderTest)
.queryById(0L);
简单入门
示例
- 示例一:
@Test
public void list() {
// mock creation 创建mock对象
List mockedList = mock(List.class);
// using mock object 使用mock对象
mockedList.add("one");
mockedList.clear();
// verification 验证
verify(mockedList).add("one");
verify(mockedList).clear();
}
@Test
public void linkedList() {
LinkedList mockLinkedList = mock(LinkedList.class);
RuntimeException runtimeException = new RuntimeException();
// 打桩测试
when(mockLinkedList.get(0)).thenReturn("first");
when(mockLinkedList.get(1)).thenReturn(runtimeException);
// 验证结果
Assertions.assertEquals("first", mockLinkedList.get(0));
Assertions.assertEquals(runtimeException, mockLinkedList.get(1));
// 验证
verify(mockLinkedList, times(1)).get(1);
}
实战案例
案例一:CRUD操作
UserDao
public class UserDao {
public User queryByName(String name) {
System.out.println(name);
return User.builder().id(5).name(name).build();
}
public Integer save(User user) {
return 1;
}
public void update(User user) {}
}
User
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User {
private Integer id;
private String name;
}
UserService
public class UserService {
private UserDao userDao = new UserDao();
/**
* 新增一个用户
*
* @param name
* @return
*/
public Integer save(String name) throws Exception {
if (StrUtil.isBlank(name)) {
throw new ValidationException("名称不能为空");
}
User user = userDao.queryByName(name);
if (user != null) {
throw new Exception("用户已存在,请不要重复添加");
}
Integer id = userDao.save(user);
return id;
}
}
UserServiceTest
class UserServiceTest {
@Mock private UserDao userDao;
@InjectMocks @Spy private UserService userService;
@BeforeEach
void before() {
MockitoAnnotations.openMocks(this);
}
@Test
void save() throws Exception {
String name = null;
// 名字为空
try {
userService.save(name);
Assertions.fail("这里会挂");
} catch (Exception e) {
Assertions.assertTrue(e instanceof ValidationException);
}
// 设置值走正常流程
name = "gzwen";
// 正常流程
Mockito.doReturn(null).when(userDao).queryByName(name);
Mockito.when(userService.save(name)).thenReturn(1);
Integer newUserId = userService.save(name);
Assertions.assertEquals(1, newUserId);
// 用户已存在,不允许重新插入,抛出异常
Mockito.doReturn(new User()).when(userDao).queryByName(name);
try {
userService.save(name);
Assertions.fail("这里会挂");
} catch (Exception e) {
Assertions.assertTrue(e instanceof Exception);
}
// 用户已存在,不允许重新插入,抛出异常;输入任意参数都返回一个空user 对象
Mockito.doReturn(new User()).when(userDao).queryByName(Mockito.any());
try {
userService.save(name);
Assertions.fail("这里会挂");
} catch (Exception e) {
Assertions.assertTrue(e instanceof Exception);
}
}
}
案例二:mock 静态方法
添加信赖
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>4.3.1</version>
<scope>test</scope>
</dependency>
静态方法:StaticUtils
public class StaticUtils {
public StaticUtils() {}
public static List<Integer> range(int tart, int end) {
return IntStream.range(tart, end).boxed().collect(Collectors.toList());
}
public static String name() {
return "Echo";
}
}
单元测试实例:StaticUtilsTest
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock private UserDao userDao;
@InjectMocks @Spy private UserService userService;
@Test
void save() throws Exception {
String name = null;
// 名字为空
try {
userService.save(name);
Assertions.fail("这里会挂");
} catch (Exception e) {
Assertions.assertTrue(e instanceof ValidationException);
}
// 设置值走正常流程
name = "gzwen";
// 正常流程
Mockito.doReturn(null).when(userDao).queryByName(name);
Mockito.when(userService.save(name)).thenReturn(1);
Integer newUserId = userService.save(name);
Assertions.assertEquals(1, newUserId);
// 用户已存在,不允许重新插入,抛出异常
Mockito.doReturn(new User()).when(userDao).queryByName(name);
try {
userService.save(name);
Assertions.fail("这里会挂");
} catch (Exception e) {
Assertions.assertTrue(e instanceof Exception);
}
// 用户已存在,不允许重新插入,抛出异常;输入任意参数都返回一个空user 对象
Mockito.doReturn(new User()).when(userDao).queryByName(Mockito.any());
try {
userService.save(name);
Assertions.fail("这里会挂");
} catch (Exception e) {
Assertions.assertTrue(e instanceof Exception);
}
}
}
其他:
如果你访问github网站比较慢,或者一些资源无法访问你可以看一下这里:网络加速器
关于我
- 关注不迷路,点赞走一波~ 转载请标注~
- 公众号