Hello 大家好,我是阿粉,日常工作中很多时候我们都需要同事间的相互配合协作完成某些功能,所以我们经常会遇到服务或者应用内不同模块之间要互相依赖的场景。比如下面的场景,
serviceA
中的
methodA()
方式依赖
serviceB
中的
methodB()
方法返回操作的结果。那如果我们要对自己的
methodA()
方法进行编写单元测试,还需要等其他同事的
methodB()
方法开发完成才行。那有没有什么办法我们可以跳过或者说模拟方法 B 的输出呢?这就引出了我们今天的主角
Mockito
,一个优秀的
Mock
测试框架。
我们通过使用
Mock
技术可以让开发不停滞,
Mock
技术的作用是将服务与服务之间的依赖在测试自测阶段隔离开,让开发人员在自己的应用内部通过模拟的方式把需要依赖外部的接口给构造出来,从而保证不被外界的开发进度所影响。今天我们要谈到的
Mockito
就是一个优秀的
Mock
框架。
Mockito
Mockito is a mocking framework that tastes really good. It lets you write beautiful tests with a clean & simple API. Mockito doesn’t give you hangover because the tests are very readable and they produce clean verification errors.
Mockito
是一个很好用的模拟框架。它让您可以使用干净简单的
API
编写漂亮的测试。
Mockito
的可读性非常好,不会让你感动迷惑,产生的验证错误也很明确。
官网地址:
https://site.mockito.org/
中文文档:
https://github.com/hehonghui/mockito-doc-zh#0
测试用例 1
首先在工程的
pom
文件里面加依赖,我们加上
mockito
和
junit
的依赖。
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
接下来我们编写一个简单的测试用例,这里我们通过
mock
一个
List
对象,先添加几个元素,后面验证添加交互是否与我们预期的一致。
@Test
public void testVerify() throws Exception {
//创建 mock 对象
List mockedList = mock(List.class);
mockedList.add("test1");
mockedList.add("test2");
mockedList.add("test2");
mockedList.clear();
//验证是否执行了一次 add("test1") 操作
verify(mockedList).add("test1");
//同上面验证是否执行了一次 add("test1") 操作,默认就是 time(1)
verify(mockedList, times(1)).add("test1");
//验证是否执行了3次 add("test2") 操作
//verify(mockedList, times(3)).add("test2");
verify(mockedList).clear();
}
上面的测试用例我们运行过后是如下效果,测试用例是通过的。
当我们放开
verify(mockedList, times(3)).add("test2");
这一行代码进行运行时,我们可以看到测试用例未通过,提示的错误是我们预期执行 3 次,结果实际只执行了 2 次
add("test2")
操作。
上面的测试用例是验证对应方式的执行次数是否和预期一致,除了有准确的次数之外,还有最多,至少,从未等验证方式,如下所示:
//精确次数
verify(mockedList, times(3)).add("test2");
//至少 1次
verify(mockedList, atLeastOnce()).add("test2");
//至少 2 次
verify(mockedList, atLeast(2)).add("test2");
//最多 5 次
verify(mockedList, atMost(5)).add("test2");
测试用例 2
通过设值或者打桩的方式预设参数,如下所示,当执行
get(0)
操作时,我们通过
thenReturn()
方法返回
hello
,当执行
get(1)
操作时我们抛出空指针异常,运行结果如下图所示:
@Test
public void testWhen() throws Exception {
LinkedList mockedList = mock(LinkedList.class);
//设置值,通常被称为打桩
when(mockedList.get(0)).thenReturn("hello");
when(mockedList.get(1)).thenThrow(new NullPointerException());
System.out.println(mockedList.get(0));
//这里会打印 "null" 因为 get(2) 没有设置
System.out.println(mockedList.get(2));
//这里会抛 exception
System.out.println(mockedList.get(1));
//验证有没有执行 get(0) 操作
verify(mockedList).get(0);
}
可以看到当我们调用
get(0)
和
get(1)
的时候控制台成功的抛出了异常。这种方式通常被称为
Stubbing
,除了使用
when...thenReturn
方式之外,还有一种形式可以表达,代码如下:
@Test
public void testDoReturn() throws Exception {
Iterator mockedList = mock(Iterator.class);
doReturn("hello").when(mockedList).next();
Object next = mockedList.next();
System.out.println(next);
doReturn("world").when(mockedList).next();
Object next2 = mockedList.next();
System.out.println(next2);
//上面的过程也可以写成如下方式
doReturn("test1", "test2").when(mockedList).next();
Object next3 = mockedList.next();
System.out.println(next3);
Object next4 = mockedList.next();
System.out.println(next4);
}
运行结果如下所示,也可以用
doThrow()
方法进行抛异常:
测试用例 3
日常开发中我们通过要保证方法的时效性,或者说我们要保证我们某个方法必须在多长时间内执行完成,这个时候我们也可以通过 mock 的方式来验证我们的方法是否满足要求。代码如下:
@Test
public void testTimeout() throws Exception {
HttpService mock = mock(HttpService.class);
String url = "http://www.xxx.com";
mock.getRequest(url);
verify(mock, timeout(100)).getRequest(url);
//timeout时间后,用自定义的检验模式验证getRequest()
VerificationMode customVer = new VerificationMode() {
@Override
public void verify(VerificationData data) {
@Override
public VerificationMode description(String s) {
return null;
verify(mock, new Timeout(100, customVer)).getRequest(url);
}
Mockito 还有很多 API 可以使用,更多的使用方式,大家可以参考这面这个网站。
https://www.tutorialspoint.com/mockito/mockito_timeouts.htm
,有更详细的介绍。
Mockito 是一个针对 Java 的单元测试模拟框架,它与 EasyMock 和 jMock 很相似,都是为了简化单元测试过程中测试上下文 ( 或者称之为测试驱动函数以及桩函数 ) 的搭建而开发的工具
PowerMock作为其他Mock框架的补充和扩展,有其自身独到的优势:final 类型的 class 和 method 的 mock 操作;完成对类方法(static)的 mock;完成对局部变量的 mock 等等。总之一句话:PowerMock 的出现为解决此类疑难问题另辟蹊径。
在某些时候,后端在开发接口的时候,处理逻辑非常复杂,在测试的时候,后端在未完成接口的情况下该如何去测试呢?
我们需要测试,但是有些请求又需要修改一下参数,或者改变一下request实现的方式,比如修改状态码,产生的图片要进行替换,或者是替换执行文件等
TDD是测试驱动开发(Test-Driven Development)的英文简称,是敏捷开发中的一项核心实践和技术,也是一种设计方法论。TDD的原理是在开发功能代码之前,先编写单元测试用例代码,测试代码确定需要编写什么产品代码。TDD虽是敏捷方法的核心实践,但不只使用于XP(Extreme Programming),同样可以适用于其他开发方法和过程。
单元测试(Unit Testing),是指对软件或项目中最小可测试单元进行正确性检验的测试工作。单元是人为规定最小可测试的功能模块,可以是一个模块,一个函数或者一个类。单元测试需要与模块开发进行隔离情况下进行测试。
单元测试(Unit Testing),是指对软件或项目中最小可测试单元进行正确性检验的测试工作。单元是人为规定最小可测试的功能模块,可以是一个模块,一个函数或者一个类。单元测试需要与模块开发进行隔离情况下进行测试。
淘系的技术发展已经有相当一段历史了,在历史的长河中总能沉淀出很多复杂的巨型项目,包罗多个业务,而且往往服务依赖比较复杂;再加上一些特殊环境变量的设置,想要在本地运行、debug 自测这种大型应用的难度越来越高;尤其是对环境不太熟悉的新人而言成本会更高。
这类应用的单元测试不能像微服务化的应用一样,可以方便的将整个 service 在本地 Run Test,但是依靠于日常开发部署环境的远程 debug、日志、Arthas 等工具定位项目自测联调中的问题又会显得格外的笨重,问题修复几秒钟,发布一次 10min 会成为严重的效率瓶颈。
如何高效的自测代码逻辑,如何不启动整个服务就能验证我的目标方
## SpringBoot应用测试
测试Springboot应用需要依赖一个非常重要的注解@SpringBootTest,这个注解会为测试用例构建Spring容器。@SpringBootTest注解修饰的测试用例默认不会启动web容器,如果需要启动web容器需要设置webEnvironment属性:
* MOCK(默认):会启动一个mock的web server,可以配合@AutoConfig