一、什么是Mock&为什么要Mock
Mock就是在测试过程中对于那些不容易构建的依赖进行模拟,以保证系统的测试流程可以正常运行,即生成一个和实际使用场景不一样的对象;
为什么需要Mock呢?
1、在测试环境中构建一些依赖的成本比较高
如一些硬件的操作,像串口和USB输入;
还有一些第三方依赖,像支付系统要调用很多外部支付网关,如果直接调用生产环境的网关就会产生脏数据的问题;
2、分而测之
以上面的例子来说,生产环境中就是要从硬件输入,那这块是不是不测试呢?肯定是要测试的,不过分开来测,一部分测试软件上的流程,即从硬件输入后系统的处理流程是否正常,在一些异常下数据是否准确;而硬件输入的测试可以由单独的团队测试,这样就可以并行测试提高效率。
二、Mockito介绍
Mockito是一款优秀的Mock工具,以下是其相关的介绍:
Mockito源码:
https://github.com/mockito/mockito
Mockito2.x新特性介绍:
https://github.com/mockito/mockito/wiki/What%27s-new-in-Mockito-2
接下来介绍Mockito的具体使用:
1、引入依赖:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.7.7</version>
<scope>test</scope>
</dependency>
2、模拟对象
具体的方式有2种:调用spy方法,或加Spy注解;
先讲下我们的范例代码,假设我们有个简单的PostService,用来操作帖子的:
public class PostService {
@Resource
private UserService userService;
/**
* 添加帖子
* @return
*/
public void addPost(Post post){
User user = userService.findUser(post.getUserId());
if (user.isDisabled()){
//用户被禁用
}
}
}
上面是个简单的例子,在添加帖子的时候需要调用用户服务来查询用户信息,这里不讨论代码组织是否合理,是否用到了DDD,只是一个例子说明如何使用Mockito;
先看看spy方法如何Mock:
import static org.mockito.Mockito.spy;
public class PostServiceTest extends TestMain {
@Autowired
private UserService userService;
@Test
public void testAddPost() throws Exception {
UserService mockUserService = spy(userService);
Post post = new Post();
post.setTitle("test");
post.setAutor("Edward");
post.setUserId(123);
Mockito.when(mockUserService .findUser(
Mockito.anyInt()).thenReturn(
new User(123, "edward", 1)
//用户ID,用户名,用户状态1:正常,0:禁用
));
}
}
Spy注解
public class PostServiceTest extends TestMain {
@Autowired
@Spy
private UserService userService;
private AutoCloseable autoCloseable;
@BeforeClass
public void initMock() {
autoCloseable = MockitoAnnotations.openMocks(this);
}
@AfterClass
public void close() throws Exception {
autoCloseable.close();
}
}
注意这里要调用MockitoAnnotations.openMocks,不然会报错;
可以看到上面拦截方法调用的语法是Mockito.when(..).thenReturn
when传方法,thenReturn传递要返回的结果,如果when要带参数,一般是这样的格式, Mockito.anyInt(),Mockito定义了很多常用类型,可以根据自己需要选择;
3、模拟静态方法
如果要用Mockito模拟静态方法,一是要保证Mockito包版本在3.4.0以上,二是需要额外加mockito-inline依赖,如下:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>3.7.7</version>
<scope>test</scope>
</dependency>
加好依赖后,通过 Mockito.mockStatic 来模拟静态方法。
@Test
public void testStaticMethod() {
LocalDate dummy = LocalDate.of(2021, 10, 4);
try (MockedStatic<LocalDate> theMock = Mockito.mockStatic(LocalDate.class)) {
theMock.when(LocalDate::now).thenReturn(dummy);
LocalDate now = LocalDate.now();
System.out.println(now);
Assert.assertEquals(2021, now.getYear());
}
}
这里要注意dummy一定要放在mockStatic前面,因为后面一旦Mock了,所有静态方法都被Mock掉,of静态方法没有设置返回值会报错的。