0%

Mock

前言:学习一下Mock测试

作用

Mock可以用来解除测试对象对外部服务的依赖(比如数据库,第三方接口等),使得测试用例可以独立运行,与JUnit结合使用,但是不可以实现对静态函数、构造函数、私有函数、Final 函数以及系统函数的模拟

实际工作中,测试可能会遇到如下情况:

  • 场景一:依赖接口不通,甲开发A模块,乙开发B模块,甲的进度比乙快,但A模块的方法依赖于B模块,要测试A模块接口怎么办?
  • 场景二:异常数据难模拟,当需要测试接口一些异常数据,接口正常情况是否无法提供异常数据的。那么如何简便地构造接口的异常数据?
  • 场景三:依赖接口性能参数无法保障。在对接口性能压测的时候,需要下游接口及时返回数据,满足上游接口的调用频度。在依赖接口多的情况下,如何减轻工作量?

比较流行的Mock有:

  • JMock
  • EasyMock
  • Mockito
  • powermock

maven依赖

1
2
3
4
5
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
</dependency>

或者

1
2
3
4
5
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.10.19</version>
</dependency>

注:mockito-all发行已经停止,Mockito 2以上版本使用mockito-core。

创建mock对象(模拟对象)

image-20191228225052348

可以对类和接口进行mock对象的创建,创建时可以为mock对象命名。对mock对象命名的好处是调试的时候容易辨认mock对象,也可以Mock对象的期望行为和返回值设定。

设置对象调用的预期返回值

通过 when(mock.someMethod()).thenReturn(value) 来设定 Mock 对象某个方法调用时的返回值

使用when(mock.someMethod()).thenThrow(new RuntimeException) 的方式来设定当调用某个方法时抛出的异常。

简单实例 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* @author shency
* @description: Test
*/
public class Test {

public static void main(String[] args) {
// 创建Mock对象,参数可以是类或者接口
List<String> list = Mockito.mock(List.class);

//设置方法的预期返回值
Mockito.when(list.get(0)).thenReturn("mock");
Mockito.when(list.get(1)).thenThrow(new RuntimeException("mock exception"));

System.out.println(list.get(0));
try{
list.get(1);
}catch (Exception e){
System.out.println(e.getMessage());
}
}
}

运行结果

image-20191228225104839

简单实例 2

现在有A、B两个模块,A需要调用B,但B未开发完成,此时使用mock测试

模块A

1
2
3
4
5
6
7
8
9
10
11
/**
* @author shency
* @description: 模块A
*/
public class AService {
public void aFunction(BService bService){
if(bService.isAccess(new Object())){
System.out.println("AService 业务操作");
}
}
}

模块B

1
2
3
4
5
6
7
8
9
10
/**
* @author shency
* @description: 模块B
*/
public class BService {
public Boolean isAccess(Object user){
System.out.println("BService 业务操作");
return null;
}
}

Test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* @author shency
* @description: Test
*/
public class Test {

public static void main(String[] args) {
AService aService = new AService();
// 创建mock对象
BService bService = Mockito.mock(BService.class);
// 当调用mock对象的方法时,任意参数均返回true
when(bService.isAccess(any())).thenReturn(true);
// 正常调用 应该BService是AService的某一属性,通过注入得到,这里是一个简单实例,所以就作为参数传入
aService.aFunction(bService);
}
}

运行结果

image-20191228225141610

成功调用了AService的方法

注解

@Mock: 创建一个Mock,感觉没什么用,还是用Mockito.mock(class)……

@InjectMocks: 创建一个实例

MockitoAnnotations.initMocks(this):自动将依赖的类注入待测类,如果依赖类在spring的管理下有自己的name,那么甚至在待测类中都不需要写setter方法

代码

XxService是XxxJob的有Spring注入的属性,先mock XxxJob的excute方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* @author shency
* @description: xxx业务单元测试
*/
public class XxxTest extends TestSupport{
@InjectMocks
@Autowired
private XxxJob xxxJob;

// 如果mock失败,可删除@Mock这个注解再试试
@Mock
private XxService xxService;

@Before
public void setUp() {
xxService = Mockito.mock(XxService.class);
when(xxService.xxxQuery(any())).thenReturn(...);
when(xxService.xxxAdd(any())).thenReturn(...);
MockitoAnnotations.initMocks(this);
}
@Test
public void executeTest(){
xxxJob.excute();
}
}
-------------本文结束感谢您的阅读-------------