官方资源
官方网站
http://mockito.org
版本介绍
还在使用 Mockito 1.x?看看 Mockito 2 有哪些新功能!Mockito 3 没有引入任何破坏性的 API 变动,但现在需要 Java 8 而不是 Mockito 2 的 Java 6。 Mockito 4 删除了过时的 API。Mockito 5 将默认 mockmaker 改为 mockito-inline,现在需要 Java 11。一次只支持一个主要版本,而且不会向旧版本回传更改内容。
项目源码
https://github.com/mockito/mockito
开发指南
添加 maven 依赖
这将在 Maven 项目中添加 Mockito 核心库的依赖关系,并限定其范围为测试(<scope>test</scope>)。这样,您就可以在单元测试中使用 Mockito 框架来模拟对象和验证行为了。请注意,您需要根据您的实际需求调整版本号。
<!-- 添加 Mockito 依赖 --> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>3.12.4</version> <scope>test</scope> </dependency>
复制代码
获得 Mockito 的推荐方法是使用自己喜欢的构建系统声明对 "mockito-core "库的依赖。使用 Gradle 可以做到这一点:
添加 Gradle 依赖
repositories { mavenCentral() }dependencies { testImplementation "org.mockito:mockito-core:3.+" }}
复制代码
Maven 用户可以声明对 mockito-core 的依赖。Mockito 会将每次更改作为 -SNAPSHOT 版本发布到公共 Sonatype 资源库。进行手动依赖关系管理的用户可直接从 Maven Central 下载 jar。使用手动依赖关系管理的传统版本可使用 1.* "mockito-all" 发行版。该发行版在 Mockito 2.* 中已停用。
mockito 需要 junit 配合使用
需要将 JUnit 和 Mockito 结合在一起使用来进行 Java 单元测试。在添加 Mockito 依赖之前,请确保您的项目中已经包含了 JUnit 的依赖。
<dependencies> <!-- 添加 JUnit 依赖 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency>
<!-- 添加 Mockito 依赖 --> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>3.12.4</version> <scope>test</scope> </dependency></dependencies>
复制代码
在上面的配置中,我们添加了 JUnit 和 Mockito 的依赖关系,并将其范围都设置为测试(<scope>test</scope>)。这样,就可以使用 JUnit 来运行测试,并使用 Mockito 进行对象模拟和行为验证。请确保根据您的实际需求调整 JUnit 和 Mockito 的版本号。
导入静态资源
为了使测试类中的代码更简洁和易读,您可以通过静态导入来引入 Mockito 和 JUnit 的一些静态资源。这样可以减少在测试类中使用这些资源时的冗余代码
import static org.mockito.Mockito.*; import static org.junit.Assert.*;
复制代码
mockito 的方法
验证互动
下面是对应相关的案例代码:
@Test public void verify_behaviour(){ //模拟创建一个List对象 List mock = mock(List.class); // 或者使用 Mockito 4.10.0+ 时更简单 List mock = mock(); // 使用 mock 对象 - 它不会抛出任何 "意外交互 "异常 mock.add(1); mock.clear(); //验证add(1)和clear()行为是否发生 // 选择性的、明确的、可读性高的验证 verify(mock).add(1); verify(mock).clear();}
复制代码
详细分析:使用 mock()模拟对象
举一个简单的案例去模型 mock 数据效果,mock 可以模拟各种各样的对象,替代真正的对象做出希望的响应。
//模拟LinkList的一个对象LinkedList mockdedList = mock(LinkedList.class);//此时条用get方法,会返回null,因为还没有对方法调用的返回值做模拟。System.out.printlin(mockedList.get(99));
复制代码
模拟方法调用的返回值
mock 对象被调用时的返回值
当我们需要模拟获取第一个元素并返回字符串“first”时,可以使用 Mockito 进行桩模拟(stub)。桩模拟是指为特定的方法调用配置返回固定值的行为。在官方文档中,这种行为被称为 stub(存根)。通过使用桩模拟,我们可以模拟本地对象以屏蔽对远程主机上对象的调用。
when(mockedList.get(0).thenReturn("first"));// 此时打印输出firstSystem.out.println(mockedList.get(0));
复制代码
详细分析:模拟,方法调用抛出异常
// 模拟获取第二个元素时,抛出RuntimeExceptionwhen(mockedList.get(1)).thenThrow(new RuntimeException);// 此时抛出RuntimeException异常System.out.println(mockedList.get(1));// 没有返回值类型的方法也可以模拟异常抛出:doThrow(new RuntimeException()).when(mockedList).clear();
复制代码
详细分析:模拟调用方法时的参数匹配
anyInt()是 Mockito 中的一个匹配器,它用于匹配任何传入的int参数。这意味着无论传入的参数是什么值,都将返回"element"。
when(mockedList.get(anyInt())).thenReturn("element");// 此时打印是elementSystem.out.println(mockedList.get(99));
复制代码
可以这样描述 anyInt()的作用:它用于接受任意的int参数,并将其值忽略,返回固定的字符串"element"。
详细分析:模拟方法调用次数
// 调用add一次mockedList.add("once");// 验证add方法是否被调用了一次,两种写法效果一样verify(mockedList)add("once");verify(mockedList,times(1)).add("once");
复制代码
可以使用 Mockito 中的atLeast(int i)和atMost(int i)来验证方法被调用的最小和最大次数限制。
可以精确地控制方法被调用的次数,并进行相应的验证。通过使用这些方法,可以更准确地断言方法的调用次数是否符合预期。
详细分析:验证被测试类是否正确工作,使用 verify()
在默认情况下,对于所有未被桩模拟过的有返回值的方法,Mockito 会返回相应的默认值。对于基本数据类型,如int,默认值是 0;对于布尔类型,默认值是false;对于其他对象类型,默认值是null。
mock 对象会覆盖整个被 mock 的对象,因此没有 stub 的方法只能返回默认值。重复 stub 两次,则以第二次为准,如下将返回”second“。
when(mockedList.get(0)).thenReturn("first");when(mockedList.get(0)).thenReturn("second");
复制代码
下面这种形式表示第一次调用返回”first“,第二次调用返回”second“,可以写 n 多个
when(mockedList.get(0)).thenReturn("first").thenReturn("second");
复制代码
如果实际调用次数超过了 stub 过的次数,则会一直返回最后一次 stub 的值,如上例,第三次调用 get(0),则返回 ”second“验证方法被调用特定的次数。
验证 add 方法被调用了两次
verify(mockedList,times(2)).add("2");
复制代码
验证 add 方法致至少被调用一次
verify(mockedList.atLeastOnce()).add("2");
复制代码
验证 add 方法至少被调用两次
verify(mockedList,atLeast(2)).add("2");
复制代码
验证 add 方法最大被调用 5 次
verify(mockedList,atMost(5)).add("2");
复制代码
验证 add 方法从未被调用
找到冗余的调用,使用 never();
verify(mockedList,never()).add("2");
复制代码
模拟所期望的结果
为了模拟获取和检索数据的行为,我们可以使用 Mockito 来进行对象模拟。通过模拟数据获取和检索的过程,我们可以更轻松地编写和执行单元测试。
// 你可以模拟具体的类,而不仅仅是接口LinkedList mockedList = mock(LinkedList.class);// 或者使用 Mockito 4.10.0+ 更简单// LinkedList mockedList = mock();// 在实际执行前出现存根when(mockedList.get(0)).thenReturn("first");// 下面将打印 "firstSystem.out.println(mockedList.get(0));// 下面打印 "null",因为 get(999) 没有被存根化System.out.println(mockedList.get(999));
复制代码
模拟所期望的结果
@Test public void when_thenReturn(){ //mock一个Iterator类 Iterator iterator = mock(Iterator.class); //预设当iterator调用next()时第一次返回hello,第n次都返回world when(iterator.next()).thenReturn("hello").thenReturn("world"); //使用mock的对象 String result = iterator.next() + " " + iterator.next() + " " + iterator.next(); //验证结果 assertEquals("hello world world",result); }
复制代码
模拟方法体抛出异常
案例一
在@Test(expected = IOException.class)注解中指定了期望的异常类型为IOException,用于断言在测试代码中是否抛出了该异常,通过指定expected注解和模拟对象的预设行为,我们可以断言在调用close()方法时是否会抛出IOException异常。
@Test(expected = IOException.class) public void when_thenThrow() throws IOException { OutputStream outputStream = mock(OutputStream.class); OutputStreamWriter writer = new OutputStreamWriter(outputStream); //预设当流关闭时抛出异常 doThrow(new IOException()).when(outputStream).close(); outputStream.close(); }
复制代码
使用doThrow()方法配置了当outputStream的close()方法被调用时抛出IOException异常的预设行为。,调用outputStream的close()方法,触发异常抛出。
案例二
在@Test(expected = RuntimeException.class)注解中指定了期望的异常类型为RuntimeException,用于断言在测试代码中是否抛出了该异常。
@Test(expected = RuntimeException.class) public void doThrow_when(){ List list = mock(List.class); doThrow(new RuntimeException()).when(list).add(1); list.add(1); }
复制代码
使用doThrow()方法配置了当list的add(1)方法被调用时抛出RuntimeException异常的预设行为,调用list的add(1)方法,触发异常抛出。通过指定expected注解和模拟对象的预设行为,我们可以断言在调用add(1)方法时是否会抛出RuntimeException异常。
验证执行顺序
下面代码用于验证在特定顺序下方法的执行状况,使用 inOrder.verify()方法按顺序验证了方法的调用。
@Test public void verification_in_order(){ List list = mock(List.class); List list2 = mock(List.class); list.add(1); list2.add("hello"); list.add(2); list2.add("world"); //将需要排序的mock对象放入InOrder InOrder inOrder = inOrder(list,list2); //下面的代码不能颠倒顺序,验证执行顺序 inOrder.verify(list).add(1); inOrder.verify(list2).add("hello"); inOrder.verify(list).add(2); inOrder.verify(list2).add("world"); }
复制代码
验证顺序
先验证 list 的 add(1)方法被调用,然后验证 list2 的 add("hello")方法被调用,接着验证 list 的 add(2)方法被调用,最后验证 list2 的 add("world")方法被调用。
通过使用 InOrder 对象,我们可以验证方法的执行顺序是否符合预期。如果按照预期顺序执行,则测试将成功通过。如果顺序不符合预期,测试将失败。
未完待续
敬请期待:【Java 技术深入解析】「核心技术提升」最流行的 Java 模拟框架 Mockito 入门指南(进阶学习案例)。
评论