Java测试框架Mockito的简明教程

什么是 Mock 测试

Mock 测试就是在测试过程中,对于某些不容易构造(如 HttpServletRequest 必须在Servlet 容器中才能构造出来)或者不容易获取比较复杂的对象(如 JDBC 中的ResultSet 对象),用一个虚拟的对象(Mock 对象)来创建以便测试的测试方法。

Mock 最大的功能是帮你把单元测试的耦合分解开,如果你的代码对另一个类或者接口有依赖,它能够帮你模拟这些依赖,并帮你验证所调用的依赖的行为。

比如一段代码有这样的依赖:

当我们需要测试A类的时候,如果没有 Mock,则我们需要把整个依赖树都构建出来,而使用 Mock 的话就可以将结构分解开,像下面这样:

Mock 对象使用范畴

真实对象具有不可确定的行为,产生不可预测的效果(如:股票行情,天气预报) :

  • 真实对象很难被创建的
  • 真实对象的某些行为很难被触发
  • 真实对象实际上还不存在的(和其他开发小组或者和新的硬件打交道)等等

使用 Mock 对象测试的关键步骤

  • 使用一个接口来描述这个对象
  • 在产品代码中实现这个接口
  • 在测试代码中实现这个接口
  • 在被测试代码中只是通过接口来引用对象,所以它不知道这个引用的对象是真实对象,还是 Mock 对象。

Mock 与Stub 的区别

Mock 不是 Stub,两者是有区别的:

  • 前者被称为 mockist TDD,而后者一般称为 classic TDD ;
  • 前者是基于行为的验证(behavior verification),后者是基于状态的验证 (state verification);
  • 前者使用的是模拟的对象,而后者使用的是真实的对象。

Java Mock 测试

目前,在 Java 阵营中主要的 Mock 测试工具有 Mockito,JMock,EasyMock 等。

关于这些框架的比较,不是本文的重点。本文着重介绍 Mockito 的使用。

Mockito 的特性

Mockito 是美味的 Java 单元测试 Mock 框架,开源。

大多 Java Mock 库如 EasyMock 或 JMock 都是 expect-run-verify (期望-运行-验证)方式,而 Mockito 则使用更简单,更直观的方法:在执行后的互动中提问。使用 Mockito,你可以验证任何你想要的。而那些使用 expect-run-verify 方式的库,你常常被迫查看无关的交互。

非 expect-run-verify 方式 也意味着,Mockito 无需准备昂贵的前期启动。他们的目标是透明的,让开发人员专注于测试选定的行为。

Mockito 拥有的非常少的 API,所有开始使用 Mockito,几乎没有时间成本。因为只有一种创造 mock 的方式。只要记住,在执行前 stub,而后在交互中验证。你很快就会发现这样 TDD java 代码是多么自然。

类似 EasyMock 的语法来的,所以你可以放心地重构。Mockito 并不需要“expectation(期望)”的概念。只有 stub 和验证。

Mockito 实现了 Gerard Meszaros 所谓的 Test Spy.

其他的一些特点:

  • 可以 mock 具体类而不单止是接口
  • 一点注解语法糖 - @Mock
  • 干净的验证错误是 - 点击堆栈跟踪,看看在测试中的失败验证;点击异常的原因来导航到代码中的实际互动。堆栈跟踪总是干干净净。
  • 允许灵活有序的验证(例如:你任意有序 verify,而不是每一个单独的交互)
  • 支持“详细的用户号码的时间”以及“至少一​​次”验证
  • 灵活的验证或使用参数匹配器的 stub (anyObject(),anyString() 或 refEq() 用于基于反射的相等匹配)
  • 允许创建自定义的参数匹配器或者使用现有的 hamcrest 匹配器

Mockito 入门

声明 mockito 依赖

Gradle 用户可以使用:

repositories { jcenter() }
dependencies { testCompile "org.mockito:mockito-core:1.+" }

示例

1.验证行为

//Let's import Mockito statically so that the code looks clearer
import static org.mockito.Mockito.*;
//mock creation
List mockedList = mock(List.class);
//using mock object
mockedList.add("one");
mockedList.clear();
//verification
verify(mockedList).add("one");
verify(mockedList).clear();

一旦创建 mock 将会记得所有的交互。你可以选择验证你感兴趣的任何交互

2.stubbing

//You can mock concrete classes, not just interfaces
LinkedList mockedList = mock(LinkedList.class);
//stubbing
when(mockedList.get(0)).thenReturn("first");
when(mockedList.get(1)).thenThrow(new RuntimeException());
//following prints "first"
System.out.println(mockedList.get(0));
//following throws runtime exception
System.out.println(mockedList.get(1));
//following prints "null" because get(999) was not stubbed
System.out.println(mockedList.get(999));
//Although it is possible to verify a stubbed invocation, usually it's just redundant
//If your code cares what get(0) returns, then something else breaks (often even before verify() gets executed).
//If your code doesn't care what get(0) returns, then it should not be stubbed. Not convinced? See here.
verify(mockedList).get(0);
  • 默认情况下,所有方法都会返回值,一个 mock 将返回要么 null,一个原始/基本类型的包装值或适当的空集。例如,对于一个 int/Integer 就是 0,而对于 boolean/Boolean 就是 false。
  • Stubbing 可以被覆盖。
  • 一旦 stub,该方法将始终返回一个 stub 的值,无论它有多少次被调用。
  • 最后的 stubbing 是很重要的 - 当你使用相同的参数 stub 多次同样的方法。换句话说:stubbing 的顺序是重要的,但它唯一有意义的却很少,例如当 stubbing 完全相同的方法调用,或者有时当参数匹配器的使用,等等。

3.参数匹配器

Mockito 验证参数值使用 Java 方式:通过使用 equals() 方法。有时,当需要额外的灵活性,可以使用参数匹配器:

//stubbing using built-in anyInt() argument matcher
when(mockedList.get(anyInt())).thenReturn("element");
//stubbing using custom matcher (let's say isValid() returns your own matcher implementation):
when(mockedList.contains(argThat(isValid()))).thenReturn("element");
//following prints "element"
System.out.println(mockedList.get(999));
//you can also verify using an argument matcher
verify(mockedList).get(anyInt());

参数匹配器允许灵活的验证或 stubbing。点击这里查看更多内置的匹配器和自定义的参数匹配器/ hamcrest匹配器的例子。

自定义参数的匹配信息,请查看 Javadoc 中 ArgumentMatcher 类。

如果你正在使用参数的匹配,所有的参数都由匹配器来提供。

下面的示例演示验证,但同样适用于 stubbing:

verify(mock).someMethod(anyInt(), anyString(), eq("third argument"));
//above is correct - eq() is also an argument matcher
verify(mock).someMethod(anyInt(), anyString(), "third argument");
//above is incorrect - exception will be thrown because third argument is given without an argument matcher.

4.调用额外的调用数字/at least x / never

//using mock
mockedList.add("once");
mockedList.add("twice");
mockedList.add("twice");
mockedList.add("three times");
mockedList.add("three times");
mockedList.add("three times");
//following two verifications work exactly the same - times(1) is used by default
verify(mockedList).add("once");
verify(mockedList, times(1)).add("once");
//exact number of invocations verification
verify(mockedList, times(2)).add("twice");
verify(mockedList, times(3)).add("three times");
//verification using never(). never() is an alias to times(0)
verify(mockedList, never()).add("never happened");
//verification using atLeast()/atMost()
verify(mockedList, atLeastOnce()).add("three times");
verify(mockedList, atLeast(2)).add("five times");
verify(mockedList, atMost(5)).add("three times");

times(1) 是默认的,因此,使用的 times(1) 可以显示的省略。

5.Stubbing void 方法处理异常

doThrow(new RuntimeException()).when(mockedList).clear();
//following throws RuntimeException:
mockedList.clear();

6.有序的验证

// A. Single mock whose methods must be invoked in a particular order
List singleMock = mock(List.class);
//using a single mock
singleMock.add("was added first");
singleMock.add("was added second");
//create an inOrder verifier for a single mock
InOrder inOrder = inOrder(singleMock);
//following will make sure that add is first called with "was added first, then with "was added second"
inOrder.verify(singleMock).add("was added first");
inOrder.verify(singleMock).add("was added second");
// B. Multiple mocks that must be used in a particular order
List firstMock = mock(List.class);
List secondMock = mock(List.class);
//using mocks
firstMock.add("was called first");
secondMock.add("was called second");
//create inOrder object passing any mocks that need to be verified in order
InOrder inOrder = inOrder(firstMock, secondMock);
//following will make sure that firstMock was called before secondMock
inOrder.verify(firstMock).add("was called first");
inOrder.verify(secondMock).add("was called second");
// Oh, and A + B can be mixed together at will

有序验证是为了灵活 - 你不必一个接一个验证所有的交互。

此外,您还可以通过创建 InOrder 对象传递只与有序验证相关的 mock 。

7. 确保 mock 上不会发生交互

//using mocks - only mockOne is interacted
mockOne.add("one");
//ordinary verification
verify(mockOne).add("one");
//verify that method was never called on a mock
verify(mockOne, never()).add("two");
//verify that other mocks were not interacted
verifyZeroInteractions(mockTwo, mockThree);

8.寻找多余的调用

//using mocks
mockedList.add("one");
mockedList.add("two");
verify(mockedList).add("one");
//following verification will fail
verifyNoMoreInteractions(mockedList);

注意:不建议 verifyNoMoreInteractions() 在每个测试方法中使用。 verifyNoMoreInteractions() 是从交互测试工具包一个方便的断言。只有与它的相关时才使用它。滥用它导致难以维护。

9. 标准创建 mock 方式 - 使用 @Mock 注解

  • 最小化可重用 mock 创建代码
  • 使测试类更加可读性
  • 使验证错误更加易读,因为字段名称用于唯一识别 mock
public class ArticleManagerTest {
@Mock private ArticleCalculator calculator;
@Mock private ArticleDatabase database;
@Mock private UserProvider userProvider;
private ArticleManager manager;

在基础类或者测试 runner 里面,使用如下:

MockitoAnnotations.initMocks(testClass);

可以使用内建 runner: MockitoJUnitRunner 或者 rule: MockitoRule

10. Stubbing 连续调用(迭代器式的 stubbing)

when(mock.someMethod("some arg"))
.thenThrow(new RuntimeException())
.thenReturn("foo");
//First call: throws runtime exception:
mock.someMethod("some arg");
//Second call: prints "foo"
System.out.println(mock.someMethod("some arg"));
//Any consecutive call: prints "foo" as well (last stubbing wins).
System.out.println(mock.someMethod("some arg"));

下面是一个精简版本:

when(mock.someMethod("some arg"))
.thenReturn("one", "two", "three");

11. 回调 Stubbing

允许使用泛型 Answer 接口。

然而,这是不包括在最初的 Mockito 另一个有争议的功能。我们建议您只需用thenReturn() 或 thenThrow() 来 stubbing ,这在测试/测试驱动中应用简洁与简单的代码足够了。但是,如果你有一个需要 stub 到泛型 Answer 接口,这里是一个例子:

when(mock.someMethod(anyString())).thenAnswer(new Answer() {
Object answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
Object mock = invocation.getMock();
return "called with arguments: " + args;
}
});
//the following prints "called with arguments: foo"
System.out.println(mock.someMethod("foo"));

12. doReturn()|doThrow()| doAnswer()|doNothing()|doCallRealMethod() 家族方法

Stubbing void 方法,需要不同的 when(Object) ,因为编译器不喜欢括号内无效的方法…

在 用于 Stubbing void 方法中,doThrow(Throwable…) 取代 stubVoid(Object)。主要原因是提高可读性和与 doAnswer() 保持一致性。

当你想用 stub void 方法 使用 doThrow():

doThrow(new RuntimeException()).when(mockedList).clear();
//following throws RuntimeException:
mockedList.clear();

在调用 when() 的相应地方可以使用 oThrow(), doAnswer(), doNothing(), doReturn() 和 doCallRealMethod(),当:

  • stub void 方法
  • stub 方法在 spy 对象(见下面)
  • 可以不止一次的 stub 相同的方法,在测试的中期来改变 mock 的行为

但你更加倾向于使用这些方法来代替 when(),在所有的 stubbing 调用。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Java中的异常测试框架JUnit使用上手指南

    JUnit是由 Erich Gamma 和 Kent Beck 编写的一个回归测试框架(regression testing framework).Junit测试是程序员测试,即白盒测试.该项目主页:http://www.junit.org/ 使用JUnit时,主要都是通过继承TestCase类别来撰写测试用例,使用testXXX()名称来撰写单元测试. 用JUnit写测试真正所需要的就三件事: 1.  一个import语句引入所有junit.framework.*下的类. 2.  一个exte

  • java单元测试JUnit框架原理与用法实例教程

    本文实例讲述了java单元测试JUnit框架原理与用法.分享给大家供大家参考,具体如下: 1 简介 JUnit是一个Java语言的单元测试框架,它由 Kent Beck 和 Erich Gamma 建立,逐渐成为 xUnit 家族中最为成功的一个. JUnit有它自己的JUnit扩展生态圈,多数Java的开发环境都已经集成了JUnit作为单元测试的工具.在这里,一个单元可以是一个方法.类.包或者子系统.因此,单元测试是指对代码中的最小可测试单元进行检查和验证,以便确保它们正常工作.例如,我们可以

  • 详解Java单元测试Junit框架实例

    问题: 1.目前测试存在的问题 2.Junit注意的细节 3.Junit使用规范 4.断言 5.案例 junit(单元测试框架) 1.目前存在的问题 1.目前的测试方法如果需要测试,都需要在main方法上调用 2.目前的结果都需要我们人工比对 2.Junit 注意的细节 1.如果使用junit测试一个方法的时候,在junit窗口上显示绿色那么表示测试正确,如果显示了红色,则代表该方法测试出现了异常不通过 2.如果点击方法名.类名.包名.工程名运行junit分别测试的是对于的方法,类.包中的所有类

  • Java测试框架Mockito的简明教程

    什么是 Mock 测试 Mock 测试就是在测试过程中,对于某些不容易构造(如 HttpServletRequest 必须在Servlet 容器中才能构造出来)或者不容易获取比较复杂的对象(如 JDBC 中的ResultSet 对象),用一个虚拟的对象(Mock 对象)来创建以便测试的测试方法. Mock 最大的功能是帮你把单元测试的耦合分解开,如果你的代码对另一个类或者接口有依赖,它能够帮你模拟这些依赖,并帮你验证所调用的依赖的行为. 比如一段代码有这样的依赖: 当我们需要测试A类的时候,如果

  • java测试框架的方法

    项目开发过程中使用的单元测试框架有Junit.TestNG以及Mockito,Junit和TestNG使用的比较多,Mockito最近才开始使用. TestNG与JUnit的相同点 1. 使用annotation,且大部分annotation相同. 2. 都可以进行单元测试(Unit test). 3. 都是针对Java测试的工具. TestNG与JUnit的不同点: 1. JUnit只能进行单元测试,TestNG可以进行单元测试,功能测试,端到端测试,集成测试等. 2. TestNG需要一个额

  • pytest测试框架+allure超详细教程

    目录 1.测试识别和运行 2.参数化 3.测试报告美化-allure 1.测试识别和运行 文件识别: 在给定的目录中,搜索所有test_.py或者_test.py文件 用例识别: Test*类包含的所有test_*的方法(测试类不能有__init__方法) 不在类中的所有test_*方法 pytest也能执行unit test写的用例和方法 运行方式1.pycharm页面修改默认的测试运行方式settings页面,输入pytest,修改Default test runner 2.右键执行pyth

  • Windows下安装Django框架的方法简明教程

    本文实例讲述了Windows下安装Django框架的方法.分享给大家供大家参考,具体如下: 在idea上运行Python项目时,出现了如下错误,这是因为系统中只安装了Python,没有安装Django,有童鞋可能会问,什么是Django,博主的建议是去问度年或谷老师 既然报错的原因就是缺少Django,那我们现在就开始安装这个应用框架. 本站下载Django1.6:Django-1.6.11.tar.gz 官网下载: Django-1.6.11.tar.gz || Django-1.7.11.t

  • Java Mybatis框架入门基础教程

    一.Mybatis介绍 MyBatis是一款一流的支持自定义SQL.存储过程和高级映射的持久化框架.MyBatis几乎消除了所有的JDBC代码,也基本不需要手工去 设置参数和获取检索结果.MyBatis能够使用简单的XML格式或者注解进行来配置,能够映射基本数据元素.Map接口和POJOs(普通java对象)到数据库中的记录. 二.MyBatis工作流程 (1)加载配置并初始化 触发条件:加载配置文件 配置来源于两个地方,一处是配置文件,一处是Java代码的注解,将SQL的配置信息加载成为一个个

  • Java高并发测试框架JCStress详解

    前言 如果要研究高并发,一般会借助高并发工具来进行测试.JCStress(Java Concurrency Stress)它是OpenJDK中的一个高并发测试工具,它可以帮助我们研究在高并发场景下JVM,类库以及硬件等状况. JCStress学起来很简单,而且官方也提供了许多高并发场景下的测试用例,只要引入一个jar包,即可运行研究. 如何使用JCStress 此演示用maven工程,首先需要引入jar包,核心包是必须要的,样例包非必须要,此是为了演示其中的例子. <dependencies>

  • Java持久层框架Mybatis入门详细教程

    mybatis介绍 mybatis它是轻量级持久层框架,由ibatis演化而来.它自动连接数据库,将数据库的结果集封装到对象中POJO. POJO: 一个简单的Java类,这个类没有实现/继承任何特殊的java接口或者类,不遵循任何主要java模型,约定或者框架的java对象.在理想情况下,POJO不应该有注解. JavaBean: JavaBean是可序列化的,实现了serializable接口 具有一个无参构造器 有按照命名规范的set和gett,is(可以用于访问布尔类型的属性)方法 My

  • google c++程序测试框架googletest使用教程详解

    目录 什么是googletest? googletest简介 谁在使用 GoogleTest? 相关开源项目 googletest的下载与编译 cmake gui编译 在vs2019中使用googletest GTest的一些基本概念 GTest的断言 事件机制 参数化 什么是googletest? googletest简介 ​GoogleTest 是 Google 的 C++ 测试和模拟框架,可以帮助程序员测试C++程序的结果预期,GoogleTest 的代码用cmake管理,可以使用cmak

  • Java优秀测试框架TestNG详解

    目录 我们为什么需要TestNG? TestNG搭建 TestNG注解及功能 TestNG配置文件 1.新增配置文件 2.配置测试套件(suit) 3.配置测试案例 TestNG测试 1.异常测试 2.忽略测试 3.超时测试 4.参数化测试 4.1使用XML配置文件提供 4.2使用@DataProvider传递参数 5.依赖测试 TestNG测试报告 总结 我们为什么需要TestNG? Java有好几个测试框架,JUnit是比较常见的一个,Spring系列默认的测试框架就是JUnit.TestN

  • NodeJS测试框架mocha入门教程

    NodeJS里最常用的测试框架估计就是mocha了.它支持多种node的assert libs, 同时支持异步和同步的测试,同时支持多种方式导出结果,也支持直接在browser上跑Javascript代码测试. 本文示例大多源于官网示例,部分示例结合需要或自己的感想有所改动.更多介绍请看 官方网址:Mocha on Github Installation: 当你成功安装nodejs v0.10 和 npm后执行下面这条命令. # npm install -g mocha p.s. Ubuntu的

随机推荐