为了继续改进 Mockito 并进一步改善单元测试体验,我们希望您升级到 2.1.0!Mockito 遵循语义版本控制,仅在主要版本升级时包含重大更改。在库的生命周期中,重大更改是推出一组全新功能所必需的,这些功能会改变现有行为甚至更改 API。有关新版本(包括不兼容更改)的综合指南,请参阅“ Mockito 2 中的新功能”维基页面。我们希望您喜欢 Mockito 2!
0.1. Mockito Android 支持 链接图标
使用 Mockito 2.6.1 版,我们提供了“原生”Android 支持。要启用 Android 支持,请将“mockito-android”库作为依赖项添加到您的项目中。此工件已发布到同一个 Mockito 组织,可以按如下方式导入 Android:
repositories {mavenCentral()}dependencies {testCompile "org.mockito:mockito-core:+"androidTestCompile "org.mockito:mockito-android:+"}
您可以在“testCompile”范围内使用“mockito-core”工件继续在常规 VM 上运行相同的单元测试,如上所示。请注意,由于 Android VM 的限制,您无法在 Android 上使用内联模拟生成器。如果您在 Android 上遇到模拟问题,请在官方问题跟踪器上打开问题 。请提供您正在使用的 Android 版本和项目的依赖项。
0.2.无需配置的内联模拟制作 链接图标
从 2.7.6 版开始,我们提供了“mockito-inline”工件,无需配置 MockMaker 扩展文件即可实现内联模拟制作。要使用此功能,请添加“mockito-inline”工件而不是“mockito-core”工件,如下所示:
repositories {mavenCentral()}dependencies {testCompile "org.mockito:mockito-inline:+"}
请注意,从 5.0.0 开始,内联模拟制作器成为默认模拟制作器,并且该工件可能会在未来版本中被废除。
有关内联模拟制作的更多信息,请参阅第 39 节。
0.3.明确设置用于内联模拟的检测 (Java 21+) 链接图标
从 Java 21 开始,JDK 限制了库将 Java 代理附加到其自己的 JVM 的能力。 因此,如果没有明确设置启用检测,inline-mock-maker 可能无法运行,并且 JVM 将始终显示警告。
要在测试执行期间明确附加 Mockito,需要将库的 jar 文件指定为-javaagent 执行 JVM 的参数。要在 Gradle 中启用此功能,以下示例将 Mockito 添加到所有测试任务中:
val mockitoAgent = configurations.create("mockitoAgent")dependencies {testImplementation(libs.mockito)mockitoAgent(libs.mockito) { isTransitive = false }}tasks {test {jvmArgs("-javaagent:${mockitoAgent.asPath}")}}
假设Mockito在版本目录中声明如下
[versions]mockito = "5.14.0"[libraries]mockito = { module = "org.mockito:mockito-core", version.ref = "mockito" }
要将 Mockito 作为代理添加到 Maven 的 surefire 插件中,需要进行以下配置:
<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-dependency-plugin</artifactId><executions><execution><goals><goal>properties</goal></goals></execution></executions></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><configuration><argLine>@{argLine} -javaagent:${org.mockito:mockito-core:jar}</argLine></configuration></plugin>
1.让我们验证一些行为
以下示例模拟了一个 List,因为大多数人都熟悉该接口(例如 add()、get()、clear()方法)。
实际上,请不要模拟 List 类。请改用真实实例。
//Let's import Mockito statically so that the code looks clearerimport static org.mockito.Mockito.*;//mock creationList mockedList = mock(List.class);//using mock objectmockedList.add("one");mockedList.clear();//verificationverify(mockedList).add("one");verify(mockedList).clear();
一旦创建,模拟就会记住所有交互。然后,您可以选择性地验证您感兴趣的任何交互。
2.来点存值怎么样
//You can mock concrete classes, not just interfacesLinkedList mockedList = mock(LinkedList.class);//stubbingwhen(mockedList.get(0)).thenReturn("first");when(mockedList.get(1)).thenThrow(new RuntimeException());//following prints "first"System.out.println(mockedList.get(0));//following throws runtime exceptionSystem.out.println(mockedList.get(1));//following prints "null" because get(999) was not stubbedSystem.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.verify(mockedList).get(0);
默认情况下,对于所有返回值的方法,模拟将根据情况返回 null、原始/原始包装器值或空集合。例如,对于 int/Integer 返回 0,对于 boolean/Boolean 返回 false。
存根可以被覆盖:例如,常见的存根可以转到夹具设置,但测试方法可以覆盖它。请注意,覆盖存根是一种潜在的代码异味,指出了过多的存根
一旦被存根,该方法将始终返回一个存根值,无论它被调用多少次。
最后的存根更为重要 - 当您多次使用相同的参数存根相同的方法时。换句话说:存根的顺序很重要,但它很少有意义,例如当存根完全相同的方法调用或有时使用参数匹配器时,等等。
3.参数匹配器
Mockito 以自然的 Java 风格验证参数值:通过使用equals()方法。有时,当需要额外的灵活性时,您可能会使用参数匹配器:
//stubbing using built-in anyInt() argument matcherwhen(mockedList.get(anyInt())).thenReturn("element");//stubbing using custom matcher (let's say isValid() returns your own matcher implementation):when(mockedList.contains(argThat(isValid()))).thenReturn(true);//following prints "element"System.out.println(mockedList.get(999));//you can also verify using an argument matcherverify(mockedList).get(anyInt());//argument matchers can also be written as Java 8 Lambdasverify(mockedList).add(argThat(someString -> someString.length() > 5));
参数匹配器允许灵活的验证或存根。 查看更多内置匹配器和自定义参数匹配器/ hamcrest 匹配器的示例。 Click here or here
有关自定义参数匹配器的信息请查看ArgumentMatcher类的 javadoc。
合理使用复杂的参数匹配。equals()偶尔使用anyX()匹配器的自然匹配风格往往会产生干净而简单的测试。有时最好重构代码以允许equals()匹配,甚至实现equals()方法来帮助测试。
另外,请阅读第 15 节或ArgumentCaptor类的 javadoc。 ArgumentCaptor是参数匹配器的特殊实现,用于捕获参数值以进行进一步的断言。
参数匹配器警告:
如果您使用参数匹配器,则所有参数都必须由匹配器提供。
下面的示例显示了验证,但同样适用于存根:
verify(mock).someMethod(anyInt(), anyString(), eq("third argument"));//above is correct - eq() is also an argument matcherverify(mock).someMethod(anyInt(), anyString(), "third argument");//above is incorrect - exception will be thrown because third argument is given without an argument matcher.
匹配器方法(例如any())eq() 不返回匹配器。在内部,它们在堆栈上记录匹配器并返回虚拟值(通常为 null)。此实现是由于 Java 编译器强加的静态类型安全性。结果是您不能在已验证/存根方法之外 使用any()、方法。eq()
4.验证确切的调用次数/ 至少 x / 从不
//using mockmockedList.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 defaultverify(mockedList).add("once");verify(mockedList, times(1)).add("once");//exact number of invocations verificationverify(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, atMostOnce()).add("once");verify(mockedList, atLeastOnce()).add("three times");verify(mockedList, atLeast(2)).add("three times");verify(mockedList, atMost(5)).add("three times");
times(1) 是默认值。因此,可以省略显式使用 times(1)。
5.使用异常来对 void 方法进行存根
doThrow(new RuntimeException()).when(mockedList).clear();//following throws RuntimeException:mockedList.clear();
请参阅第 12 节中有关doThrow()|方法系列的 更多信息。 doAnswer()
6.按顺序验证
// A. Single mock whose methods must be invoked in a particular orderList singleMock = mock(List.class);//using a single mocksingleMock.add("was added first");singleMock.add("was added second");//create an inOrder verifier for a single mockInOrder 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 orderList firstMock = mock(List.class);List secondMock = mock(List.class);//using mocksfirstMock.add("was called first");secondMock.add("was called second");//create inOrder object passing any mocks that need to be verified in orderInOrder inOrder = inOrder(firstMock, secondMock);//following will make sure that firstMock was called before secondMockinOrder.verify(firstMock).add("was called first");inOrder.verify(secondMock).add("was called second");// Oh, and A + B can be mixed together at will
按顺序验证是灵活的 -您不必逐一验证所有交互,而只需按顺序验证您感兴趣的测试。
另外,您可以创建一个 InOrder 对象,仅传递与按顺序验证相关的模拟。
7.确保交互永远不会在模拟中发生
//using mocks - only mockOne is interactedmockOne.add("one");//ordinary verificationverify(mockOne).add("one");//verify that method was never called on a mockverify(mockOne, never()).add("two");8.查找冗余调用 链接图标//using mocksmockedList.add("one");mockedList.add("two");verify(mockedList).add("one");//following verification will failverifyNoMoreInteractions(mockedList);
警告:一些进行过大量经典的 expect-run-verify 模拟的用户倾向于非常频繁地使用,verifyNoMoreInteractions()甚至在每种测试方法中都使用。 verifyNoMoreInteractions()不建议在每种测试方法中使用。 verifyNoMoreInteractions()是来自交互测试工具包的便捷断言。仅在相关时使用它。滥用它会导致过度指定、更难维护的测试。
另请参阅never()- 它更加明确,可以很好地传达意图。
9.模拟创建的简写 -@Mock注释
最大限度地减少重复的模拟创建代码。
使测试类更具可读性。
由于字段名称 用于识别模拟,因此使验证错误更易于阅读。
public class ArticleManagerTest {@Mock private ArticleCalculator calculator;@Mock private ArticleDatabase database;@Mock private UserProvider userProvider;private ArticleManager manager;@org.junit.jupiter.api.Testvoid testSomethingInJunit5(@Mock ArticleDatabase database) {
重要!这需要位于基类或测试运行器中的某个位置:
MockitoAnnotations.openMocks(testClass);
您可以使用内置运行器:MockitoJUnitRunner或规则:MockitoRule。对于 JUnit5 测试,请参阅第 45 节中描述的 JUnit5 扩展。
更多详情请阅读:MockitoAnnotations
10.对连续调用进行存根(迭代器样式存根)
有时我们需要对同一方法调用使用不同的返回值/异常进行存根。典型的用例可能是模拟迭代器。Mockito 的原始版本没有此功能来促进简单的模拟。例如,可以使用迭代器Iterable或简单的集合来代替。这些提供了自然的存根方式(例如使用真实集合)。但在极少数情况下,对连续调用进行存根可能会很有用:
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");
警告:如果不是链接.thenReturn()调用,而是使用相同匹配器或参数的多个存根,则每个存根将覆盖前一个存根:
//All mock.someMethod("some arg") calls will return "two"when(mock.someMethod("some arg")).thenReturn("one")when(mock.someMethod("some arg")).thenReturn("two")
11.使用回调进行存根
允许使用通用Answer接口进行存根。
这是 Mockito 最初未包含的另一个有争议的功能。我们建议简单地使用thenReturn()或 进行存根thenThrow(),这足以测试/测试驱动任何干净简单的代码。但是,如果您确实需要使用通用 Answer 接口进行存根,以下是示例:
when(mock.someMethod(anyString())).thenAnswer(new Answer() {public Object answer(InvocationOnMock invocation) {Object[] args = invocation.getArguments();Object mock = invocation.getMock();return "called with arguments: " + Arrays.toString(args);}});//Following prints "called with arguments: [foo]"System.out.println(mock.someMethod("foo"));
- doReturn()| doThrow()| doAnswer()| doNothing()|doCallRealMethod()方法系列
对 void 方法进行存根需要采用不同的方法,when(Object)因为编译器不喜欢括号内的 void 方法……
doThrow()当您想要存根带有异常的 void 方法时 使用:
doThrow(new RuntimeException()).when(mockedList).clear();//following throws RuntimeException:mockedList.clear();
对于任何方法, 您 都可以使用doThrow()、doAnswer()、 和代替 的相应调用。当您doNothing()doReturn()doCallRealMethod()when()
存根无效方法
间谍对象上的存根方法(见下文)
多次存根相同的方法,以在测试过程中改变模拟的行为。
when()但是您可能更喜欢在所有存根调用中 使用这些方法来代替替代方法。
阅读有关这些方法的更多信息:
doReturn(Object)doThrow(Throwable...)doThrow(Class)doAnswer(Answer)doNothing()doCallRealMethod()
13.监视真实物体
您可以创建真实对象的间谍。使用间谍时,将调用真实方法(除非方法被存根)。
真正的间谍应该谨慎且偶尔地 使用,例如在处理遗留代码时。
监视真实对象可能与“部分模拟”概念有关。 在 1.8 版之前,Mockito 间谍并不是真正的部分模拟。原因是我们认为部分模拟是一种代码异味。在某个时候,我们发现了部分模拟的合法用例(第三方接口、遗留代码的临时重构)。
List list = new LinkedList();List spy = spy(list);//optionally, you can stub out some methods:when(spy.size()).thenReturn(100);//using the spy calls *real* methodsspy.add("one");spy.add("two");//prints "one" - the first element of a listSystem.out.println(spy.get(0));//size() method was stubbed - 100 is printedSystem.out.println(spy.size());//optionally, you can verifyverify(spy).add("one");verify(spy).add("two");
监视真实物体的重要提
有时,使用存根间谍是不可能或不切实际的when(Object)。因此,在使用间谍时,请考虑使用doReturn| Answer|Throw()系列方法进行存根。示例:
List list = new LinkedList();List spy = spy(list);//Impossible: real method is called so spy.get(0) throws IndexOutOfBoundsException (the list is yet empty)when(spy.get(0)).thenReturn("foo");//You have to use doReturn() for stubbingdoReturn("foo").when(spy).get(0);
Mockito 不将调用委托给传递的真实实例,而是实际创建它的副本。因此,如果您保留真实实例并与其交互,不要指望被监视者知道这些交互及其对真实实例状态的影响。推论是,当在间谍上调用未存根的方法,但在真实实例上没有调用时,您将看不到对真实实例的任何影响。
注意 final 方法。Mockito 不会模拟 final 方法,因此底线是:当您监视真实对象 + 您尝试存根 final 方法 = 麻烦。此外,您也无法验证这些方法。
14. 更改未存根调用的默认返回值(自 1.7 版起) 链接图标
您可以创建一个模拟,并为其返回值指定策略。这是一项相当高级的功能,通常您不需要它来编写像样的测试。但是,它对于处理遗留系统很有帮助。
这是默认答案,因此仅当您不存根方法调用时才会使用它。
Foo mock = mock(Foo.class, Mockito.RETURNS_SMART_NULLS);Foo mockTwo = mock(Foo.class, new YourOwnAnswer());
阅读有关答案的这个有趣的实现的更多信息:RETURNS_SMART_NULLS
15.捕获进一步断言的论据(自 1.8.0 起) 链接图标
Mockito 以自然的 Java 风格验证参数值:通过使用equals()方法。这也是推荐的匹配参数的方式,因为它使测试简洁明了。但在某些情况下,在实际验证之后对某些参数进行断言会很有帮助。例如:
ArgumentCaptor<Person> argument = ArgumentCaptor.forClass(Person.class);verify(mock).doSomething(argument.capture());assertEquals("John", argument.getValue().getName());
警告:建议将 ArgumentCaptor 与验证一起使用,而不是与存根一起使用。将 ArgumentCaptor 与存根一起使用可能会降低测试的可读性,因为捕获器是在断言(又称验证或“then”)块之外创建的。此外,它可能会减少缺陷定位,因为如果没有调用存根方法,则不会捕获任何参数。
在某种程度上,ArgumentCaptor 与自定义参数匹配器相关(请参阅ArgumentMatcher类的 javadoc)。这两种技术都可用于确保将某些参数传递给模拟。但是,如果满足以下条件,ArgumentCaptor 可能更适合:
自定义参数匹配器不太可能被重用
你只需要它对参数值进行断言即可完成验证
通过自定义参数匹配器ArgumentMatcher通常更适合存根。
16.真正的部分模拟(自 1.8.0 起)
最后,经过多次内部辩论和邮件列表讨论,Mockito 添加了部分模拟支持。以前,我们认为部分模拟是代码异味。然而,我们发现了部分模拟的合法用例。
在 1.8 版之前, spy()它不会生成真正的部分模拟,这会让一些用户感到困惑。阅读有关监视的更多信息:此处或方法的 javadoc spy(Object)。
//you can create partial mock with spy() method:
List list = spy(new LinkedList());
//you can enable partial mock capabilities selectively on mocks:
Foo mock = mock(Foo.class);
//Be sure the real implementation is ‘safe’.
//If real implementation throws exceptions or depends on specific state of the object then you’re in trouble.
when(mock.someMethod()).thenCallRealMethod();
像往常一样,您将阅读部分模拟警告:面向对象编程通过将复杂性划分为单独的、特定的 SRPy 对象来处理复杂性。部分模拟如何适应这种范式?嗯,它就是不适合…部分模拟通常意味着复杂性已转移到同一对象上的不同方法。在大多数情况下,这不是您想要设计应用程序的方式。
然而,在极少数情况下部分模拟会派上用场:处理无法轻易更改的代码(第三方接口、遗留代码的临时重构等)。但是,我不会对新的、测试驱动的和精心设计的代码使用部分模拟。
17.重置模拟(自 1.8.0 起) 链接图标
使用此方法可能表明测试质量不佳。通常,您不需要重置模拟,只需为每种测试方法创建新的模拟即可。
请reset()考虑编写简单、小而有针对性的测试方法,而不是冗长、过度指定的测试。 第一个潜在的代码异味是reset()在测试方法的中间。这可能意味着你测试太多了。遵循测试方法的提示:“请让我们保持小规模并专注于单一行为”。mockito 邮件列表中有几个关于它的帖子。
我们添加方法的唯一原因reset()是使其能够与容器注入模拟一起使用。有关更多信息,请参阅常见问题解答(此处)。
不要伤害自己。 reset()在测试方法的中间存在代码异味(您可能测试太多了)。
List mock = mock(List.class);when(mock.size()).thenReturn(10);mock.add(1);reset(mock);//at this point the mock forgot any interactions and stubbing
18.故障排除和验证框架使用情况(自 1.8.0 起) 链接图标
首先,如果遇到任何问题,我建议您阅读 Mockito 常见问题解答: https://github.com/mockito/mockito/wiki/FAQ
如果有任何疑问,您也可以发帖至 mockito 邮件列表: https://groups.google.com/group/mockito
接下来,你应该知道 Mockito 会验证你是否始终正确使用它。但是,有一个问题,所以请阅读 javadocvalidateMockitoUsage()
19.行为驱动开发的别名(自1.8.0起)
行为驱动开发编写测试的风格使用//given //when //then注释作为测试方法的基本部分。这正是我们编写测试的方式,我们热烈鼓励您这样做!
从这里开始了解 BDD: https: //en.wikipedia.org/wiki/Behavior-driven_development
问题是,当前具有when字的规范角色的存根 API 无法与//given //when //then注释很好地集成。这是因为存根属于测试的给定组件,而不是测试的whenBDDMockito组件。因此,类引入了一个别名,以便您使用BDDMockito.given(Object)方法存根方法调用。现在它真的很好地与BDD 风格测试的给定组件集成!
测试可能如下所示:
import static org.mockito.BDDMockito.*;Seller seller = mock(Seller.class);Shop shop = new Shop(seller);public void shouldBuyBread() throws Exception {//givengiven(seller.askForBread()).willReturn(new Bread());//whenGoods goods = shop.buyBread();//thenassertThat(goods, containBread());}
20.可序列化模拟(自 1.8.1 版起)
模拟可以序列化。利用此功能,您可以在需要依赖项可序列化的地方使用模拟。
警告:这在单元测试中很少使用。
该行为是针对具有不可靠外部依赖关系的 BDD 规范的特定用例实现的。这是在 Web 环境中,来自外部依赖关系的对象被序列化以在各层之间传递。
要创建可序列化的模拟使用MockSettings.serializable():
List serializableMock = mock(List.class, withSettings().serializable());
假设该类满足 所有正常 序列化要求 ,则可以对模拟进行序列化。
使真实对象间谍可序列化需要付出更多努力,因为 spy(…) 方法没有接受 MockSettings 的重载版本。不用担心,您几乎不会使用它。
List<Object> list = new ArrayList<Object>();List<Object> spy = mock(ArrayList.class, withSettings().spiedInstance(list).defaultAnswer(CALLS_REAL_METHODS).serializable());
- 新增注释:@Captor、 @Spy、 @InjectMocks(自 1.8.3 起)
版本 1.8.3 带来了新的注释,有时可能会有帮助:
@Captor简化了创建ArgumentCaptor - 当捕获的参数是一个讨厌的泛型类并且你想避免编译器警告时很有用
@ Spy-你可以改用它spy(Object)。
@ InjectMocks-自动将模拟或间谍字段注入到测试对象中。
请注意,@InjectMocks也可以与 @ 注释结合使用Spy,这意味着 Mockito 会将模拟注入到测试中的部分模拟中。这种复杂性也是您应该只在万不得已的情况下使用部分模拟的另一个很好的理由。请参阅第 16 点关于部分模拟的内容。
所有新注释仅在 上处理MockitoAnnotations.openMocks(Object)。就像 @Mock注释一样,您可以使用内置的运行器:MockitoJUnitRunner或规则: MockitoRule。
22.超时验证(自 1.8.5 起)
允许超时验证。它会导致验证等待指定的一段时间以进行所需的交互,而不是在尚未发生交互时立即失败。可能对并发条件下的测试有用。
这个特性应该很少使用——找出更好的方法来测试你的多线程系统。
尚未实现与 InOrder 验证配合使用。
例子:
//passes when someMethod() is called no later than within 100 ms//exits immediately when verification is satisfied (e.g. may not wait full 100 ms)verify(mock, timeout(100)).someMethod();//above is an alias to:verify(mock, timeout(100).times(1)).someMethod();//passes as soon as someMethod() has been called 2 times under 100 msverify(mock, timeout(100).times(2)).someMethod();//equivalent: this also passes as soon as someMethod() has been called 2 times under 100 msverify(mock, timeout(100).atLeast(2)).someMethod();
23.自动实例化和构造@Spies函数 @InjectMocks注入优点(自 1.9.0 起)
Mockito 现在将尝试实例化@ Spy,并将InjectMocks使用构造函数注入、setter注入或字段注入来实例化@字段。
要利用此功能,您需要使用MockitoAnnotations.openMocks(Object)、MockitoJUnitRunner 或MockitoRule。
在 javadoc 中了解有关可用技巧和注入规则的更多信息InjectMocks
//instead:@Spy BeerDrinker drinker = new BeerDrinker();//you can write:@Spy BeerDrinker drinker;//same applies to @InjectMocks annotation:@InjectMocks LocalPub;
24.单行存根(自 1.9.0 起)
Mockito 现在允许您在存根时创建模拟。基本上,它允许在一行代码中创建一个存根。这有助于保持测试代码的整洁。例如,可以在测试中的字段初始化时创建和存根一些无聊的存根:
public class CarTest {Car boringStubbedCar = when(mock(Car.class).shiftGear()).thenThrow(EngineNotStarted.class).getMock();@Test public void should... {}
25.验证忽略存根(自 1.9.0 起)
Mockito 现在允许为了验证而忽略存根。有时与verifyNoMoreInteractions()或 验证结合使用时很有用inOrder()。有助于避免对存根调用进行冗余验证 - 通常我们对验证存根不感兴趣。
警告,ignoreStubs()可能会导致过度使用 verifyNoMoreInteractions(ignoreStubs(…)); 请记住,Mockito 不建议对每个测试都进行轰炸,verifyNoMoreInteractions() 原因在 javadoc 中有所概述verifyNoMoreInteractions(Object…)
一些例子:
verify(mock).foo();verify(mockTwo).bar();//ignores all stubbed methods:verifyNoMoreInteractions(ignoreStubs(mock, mockTwo));//creates InOrder that will ignore stubbedInOrder inOrder = inOrder(ignoreStubs(mock, mockTwo));inOrder.verify(mock).foo();inOrder.verify(mockTwo).bar();inOrder.verifyNoMoreInteractions();
高级示例和更多详细信息请参阅 javadocignoreStubs(Object…)
26.模拟细节(2.2.x 版改进)
Mockito 提供 API 来检查模拟对象的详细信息。此 API 对高级用户和模拟框架集成者很有用。
//To identify whether a particular object is a mock or a spy:Mockito.mockingDetails(someObject).isMock();Mockito.mockingDetails(someObject).isSpy();//Getting details like type to mock or default answer:MockingDetails details = mockingDetails(mock);details.getMockCreationSettings().getTypeToMock();details.getMockCreationSettings().getDefaultAnswer();//Getting invocations and stubbings of the mock:MockingDetails details = mockingDetails(mock);details.getInvocations();details.getStubbings();//Printing all interactions (including stubbing, unused stubs)System.out.println(mockingDetails(mock).printInvocations());
有关详细信息,请参阅 javadoc MockingDetails。
27.将调用委托给真实实例(自 1.9.5 起)
对于难以使用常规间谍 API 进行模拟或监视的对象的间谍或部分模拟非常有用。自 Mockito 1.10.11 起,委托可能与模拟属于同一类型,也可能不属于同一类型。如果类型不同,则需要在委托类型上找到匹配的方法,否则会引发异常。此功能的可能用例:
带有接口的最终类
已经自定义代理对象
具有 finalize 方法的特殊对象,即避免执行 2 次
与普通间谍的区别:
常规间谍 ( spy(Object)) 包含来自被监视实例的所有状态,并且方法在间谍上被调用。被监视实例仅在模拟创建时用于从中复制状态。如果您在常规间谍上调用一个方法,并且它在内部调用此间谍上的其他方法,则这些调用会被记住以进行验证,并且可以有效地对它们进行存根。
委托的模拟只是将所有方法委托给委托。委托始终被使用,因为方法被委托给它。如果您在委托的模拟上调用一个方法,并且它在内部调用此模拟上的其他方法,则这些调用不会被记住以进行验证,存根也不会对它们产生影响。委托的模拟不如常规间谍强大,但在无法创建常规间谍时很有用。
请参阅文档中的更多信息AdditionalAnswers.delegatesTo(Object)。
- MockMakerAPI(自 1.9.5 起)
在 Google Android 开发人员的要求和补丁的推动下,Mockito 现在提供了一个扩展点,允许替换代理生成引擎。默认情况下,Mockito 使用Byte Buddy 创建动态代理。
该扩展点适用于想要扩展 Mockito 的高级用户。例如,现在可以借助dexmaker使用 Mockito 进行 Android 测试。
有关更多详细信息、动机和示例,请参阅文档MockMaker。
- BDD 风格验证(自 1.10.0 起) 链接图标
通过使用 BDD then关键字 开始验证,启用行为驱动开发 (BDD) 样式验证。
given(dog.bark()).willReturn(2);// when...then(person).should(times(2)).ride(bike);
有关详细信息和示例,请参阅BDDMockito.then(Object)
30.监视或模拟抽象类(自 1.10.12 起,在 2.7.13 和 2.7.14 中进一步增强)
现在可以方便地监视抽象类。请注意,过度使用间谍程序可能会引起代码设计问题(请参阅spy(Object))。
以前,只能监视对象实例。新 API 使得在创建模拟实例时可以使用构造函数。这对于模拟抽象类特别有用,因为用户不再需要提供抽象类的实例。目前,仅支持无参数构造函数,如果不够,请告诉我们。
//convenience API, new overloaded spy() method:SomeAbstract spy = spy(SomeAbstract.class);//Mocking abstract methods, spying default methods of an interface (only available since 2.7.13)Function<Foo, Bar> function = spy(Function.class);//Robust API, via settings builder:OtherAbstract spy = mock(OtherAbstract.class, withSettings().useConstructor().defaultAnswer(CALLS_REAL_METHODS));//Mocking an abstract class with constructor arguments (only available since 2.7.14)SomeAbstract spy = mock(SomeAbstract.class, withSettings().useConstructor("arg1", 123).defaultAnswer(CALLS_REAL_METHODS));//Mocking a non-static inner abstract class:InnerAbstract spy = mock(InnerAbstract.class, withSettings().useConstructor().outerInstance(outerInstance).defaultAnswer(CALLS_REAL_METHODS));
更多信息请参阅MockSettings.useConstructor(Object…)。
31. Mockito 模拟可以跨类加载器进行序列化/反序列化(自 1.10.0 起)
Mockito 引入了跨类加载器的序列化。与任何其他形式的序列化一样,模拟层次结构中的所有类型都必须可序列化,包括答案。由于此序列化模式需要做更多工作,因此这是一个可选设置。
// use regular serializationmock(Book.class, withSettings().serializable());// use serialization across classloadersmock(Book.class, withSettings().serializable(ACROSS_CLASSLOADERS));
详情请参阅MockSettings.serializable(SerializableMode)。
32.通过深存根提供更好的通用支持(自 1.10.0 起)
深度存根已得到改进,可以查找类中可用的通用信息。这意味着可以使用此类类,而无需模拟行为。
class Lines extends List<Line> {// ...}lines = mock(Lines.class, RETURNS_DEEP_STUBS);// Now Mockito understand this is not an Object but a LineLine line = lines.iterator().next();
请注意,在大多数情况下,返回模拟的模拟是错误的。
33. Mockito JUnit 规则(自 1.10.17 起)
Mockito 现在提供 JUnit 规则。到目前为止, 在 JUnit 中有两种方法可以初始化由 Mockito 注释注释的字段,例如、、等。 @Mock@Spy@InjectMocks
使用注释 JUnit 测试类@RunWith(MockitoJUnitRunner.class)
方法MockitoAnnotations.openMocks(Object)中调用@Before
现在您可以选择使用一条规则:
@RunWith(YetAnotherRunner.class)public class TheTest {@Rule public MockitoRule mockito = MockitoJUnit.rule();// ...}
更多信息请参阅MockitoJUnit.rule()。
34.打开或关闭插件 (自1.10.15 起)
mockito 中正在酝酿一项功能,允许切换 mockito 插件。更多信息请见此处PluginSwitch。
35.自定义验证失败信息(自 2.1.0 起) 链接图标
允许指定在验证失败时打印的自定义消息。
例子:
// will print a custom message on verification failureverify(mock, description("This will print on failure")).someMethod();// will work with any verification modeverify(mock, times(2).description("someMethod should be called twice")).someMethod();
- Java 8 Lambda Matcher 支持(自 2.1.0 起)
您可以使用 Java 8 lambda 表达式ArgumentMatcher来减少对 的依赖ArgumentCaptor。如果您需要验证模拟函数调用的输入是否正确,那么您通常会使用ArgumentCaptor来查找使用的操作数,然后对它们进行后续断言。虽然对于复杂的示例,这可能很有用,但它也很冗长。
编写 lambda 来表达匹配非常简单。当与 argThat 结合使用时,函数的参数将作为强类型对象传递给 ArgumentMatcher,因此可以对其进行任何操作。
例子:
// verify a list only had strings of a certain length added to it// note - this will only compile under Java 8verify(list, times(2)).add(argThat(string -> string.length() < 5));// Java 7 equivalent - not as neatverify(list, times(2)).add(argThat(new ArgumentMatcher<String>(){public boolean matches(String arg) {return arg.length() < 5;}}));// more complex Java 8 example - where you can specify complex verification behaviour functionallyverify(target, times(1)).receiveComplexObject(argThat(obj -> obj.getSubObject().get(0).equals("expected")));// this can also be used when defining the behaviour of a mock under different inputs// in this case if the input list was fewer than 3 items the mock returns nullwhen(mock.someMethod(argThat(list -> list.size()<3))).thenReturn(null);
- Java 8 自定义答案支持(自 2.1.0 起)
由于该Answer接口只有一个方法,因此在 Java 8 中已经可以使用 lambda 表达式来实现它,适用于非常简单的情况。您需要使用方法调用的参数越多,您就越需要对 中的参数进行类型转换InvocationOnMock。
例子:
// answer by returning 12 every timedoAnswer(invocation -> 12).when(mock).doSomething();// answer by using one of the parameters - converting into the right// type as your go - in this case, returning the length of the second string parameter// as the answer. This gets long-winded quickly, with casting of parameters.doAnswer(invocation -> ((String)invocation.getArgument(1)).length()).when(mock).doSomething(anyString(), anyString(), anyString());
为了方便起见,可以编写自定义答案/操作,这些答案/操作使用方法调用的参数,就像 Java 8 lambda 一样。即使在 Java 7 及更低版本中,这些基于类型化接口的自定义答案也可以减少样板。特别是,这种方法将使测试使用回调的函数变得更容易。方法AdditionalAnswers.answer(Answer1)} 和AdditionalAnswers.answerVoid(VoidAnswer1) 可用于创建答案。它们依赖于 org.mockito.stubbing 中支持最多 5 个参数的答案的相关答案接口。
例子:
// Example interface to be mocked has a function like:void execute(String operand, Callback callback);// the example callback has a function and the class under test// will depend on the callback being invokedvoid receive(String item);// Java 8 - style 1doAnswer(AdditionalAnswers.<String,Callback>answerVoid((operand, callback) -> callback.receive("dummy"))).when(mock).execute(anyString(), any(Callback.class));// Java 8 - style 2 - assuming static import of AdditionalAnswersdoAnswer(answerVoid((String operand, Callback callback) -> callback.receive("dummy"))).when(mock).execute(anyString(), any(Callback.class));// Java 8 - style 3 - where mocking function to is a static member of test classprivate static void dummyCallbackImpl(String operation, Callback callback) {callback.receive("dummy");}doAnswer(answerVoid(TestClass::dummyCallbackImpl)).when(mock).execute(anyString(), any(Callback.class));// Java 7doAnswer(answerVoid(new VoidAnswer2<String, Callback>() {public void answer(String operation, Callback callback) {callback.receive("dummy");}})).when(mock).execute(anyString(), any(Callback.class));// returning a value is possible with the answer() function// and the non-void version of the functional interfaces// so if the mock interface had a method likeboolean isSameString(String input1, String input2);// this could be mocked// Java 8doAnswer(AdditionalAnswers.<Boolean,String,String>answer((input1, input2) -> input1.equals(input2))).when(mock).execute(anyString(), anyString());// Java 7doAnswer(answer(new Answer2<String, String, String>() {public String answer(String input1, String input2) {return input1 + input2;}})).when(mock).execute(anyString(), anyString());
38.元数据和泛型类型保留(自 2.1.0 起)
Mockito 现在会保留模拟方法和类型的注释以及通用元数据。以前,模拟类型不会保留类型的注释,除非它们被明确继承,并且永远不会保留方法的注释。因此,现在以下条件成立:
@MyAnnotationclass Foo {List<String> bar() { ... }}Class<?> mockType = mock(Foo.class).getClass();assert mockType.isAnnotationPresent(MyAnnotation.class);assert mockType.getDeclaredMethod("bar").getGenericReturnType() instanceof ParameterizedType;
使用 Java 8 时,Mockito 现在还会保留类型注释。这是默认行为,如果使用 替代方法,则可能不成立。MockMaker
39.模拟最终类型、枚举和最终方法(自 2.1.0 起)
Mockito 现在默认支持模拟 final 类和方法。这是一项了不起的改进,体现了 Mockito 不断追求改善测试体验的决心。我们的目标是让 Mockito “只适用于” final 类和方法。以前它们被认为是不可模拟的,阻止用户模拟。从 5.0.0 开始,此功能默认启用。
此替代模拟生成器结合使用了 Java 检测 API 和子类化,而不是创建新类来表示模拟。这样,就可以模拟最终类型和方法。
在 5.0.0 之前的版本中,此模拟生成器默认处于关闭状态,因为它基于完全不同的模拟机制,需要更多来自社区的反馈。它可以通过 mockito 扩展机制明确激活,只需在类路径中创建一个 /mockito-extensions/org.mockito.plugins.MockMaker包含值的文件即可mock-maker-inline。
为方便起见,Mockito 团队提供了一个预配置此模拟生成器的工件。不要使用 mockito-core工件,而是在项目中包含mockito-inline工件。请注意,一旦将最终类和方法的模拟集成到默认模拟生成器中,此工件可能会停用。
关于这个模拟制作器,有几点值得注意:
模拟最终类型和枚举与以下模拟设置不兼容:
明确序列化支持withSettings().serializable()
额外接口withSettings().extraInterfaces()
有些方法不能被模拟
包可见的方法java.*
native方法
此模拟生成器是围绕 Java Agent 运行时附件设计的;这需要兼容的 JVM,即 JDK(或 Java 9 VM)的一部分。然而,当在 Java 9 之前的非 JDK VM 上运行时,可以 在启动 JVM 时 使用参数手动添加Byte Buddy Java 代理 jar 。-javaagent
如果你对该功能的更多细节感兴趣,请阅读 org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker
- 使用“更严格”的 Mockito 提高生产力并进行更清晰的测试(自 2.+ 起)
要快速了解“更严格”的 Mockito 如何提高您的工作效率并使您的测试更清洁,请参阅:
MockitoRule.strictness(Strictness)使用JUnit4 规则进行严格存根 -Strictness.STRICT_STUBS
使用 JUnit4 Runner 进行严格存根 -MockitoJUnitRunner.Strict
使用 JUnit5 扩展进行严格存根 -org.mockito.junit.jupiter.MockitoExtension
使用 TestNG Listener MockitoTestNGListener进行严格存根
如果不能使用运行器/规则,则进行严格存根 -MockitoSession
不必要的存根检测MockitoJUnitRunner
存根参数不匹配警告,记录在MockitoHint
Mockito 默认是一个“松散”的模拟框架。模拟可以进行交互,而无需事先设定任何期望。这是有意为之,它通过强制用户明确说明他们想要存根/验证的内容来提高测试质量。它也非常直观、易于使用,并且与干净测试代码的“给定”、“何时”、“然后”模板完美融合。这也不同于过去的经典模拟框架,它们默认是“严格”的。
默认情况下“松散”使得 Mockito 测试有时更难调试。在某些情况下,错误配置的存根(例如使用错误的参数)会迫使用户使用调试器运行测试。理想情况下,测试失败是显而易见的,不需要调试器来识别根本原因。从 2.1 版开始,Mockito 获得了新功能,推动框架走向“严格”。我们希望 Mockito 提供出色的可调试性,同时不失去其核心模拟风格,并针对直观性、明确性和干净的测试代码进行了优化。
帮助 Mockito!试用新功能,向我们提供反馈,加入有关 Mockito 严格性的讨论(GitHub 问题 769)。
- 用于框架集成的高级公共 API(自 2.10.+ 起)
2017 年夏天,我们决定 Mockito 应该提供更好的 API 以实现高级框架集成。新 API 不适用于想要编写单元测试的用户。它适用于需要使用一些自定义逻辑扩展或包装 Mockito 的其他测试工具和模拟框架。在设计和实施过程中(问题 1110),我们开发并更改了以下公共 API 元素:
新MockitoPlugins功能 - 使框架集成商能够访问默认的 Mockito 插件。当需要实现自定义插件MockMaker 并将某些行为委托给默认的 Mockito 实现时,此功能非常有用。
新建- 创建 Mockito 稍后使用的模拟设置的不可变视图。对于在实现自定义时MockSettings.build(Class)使用或创建调用很有用。 InvocationFactoryMockHandler
新的MockingDetails.getMockHandler()- 其他框架可以使用模拟处理程序以编程方式模拟对模拟对象的调用。
新建MockHandler.getMockSettings()——有助于获取创建模拟对象的设置。
New InvocationFactory- 提供创建Invocation对象实例的方法。对于需要以编程方式模拟模拟对象上的方法调用的框架集成非常有用。
新MockHandler.getInvocationContainer()- 提供对没有方法(标记接口)的调用容器对象的访问。需要容器来隐藏内部实现并避免将其泄露给公共 API。
已更改Stubbing- 它现在扩展了Answer接口。它向后兼容,因为 Stubbing 接口不可扩展(请参阅NotExtensible)。对于我们的用户来说,这种更改应该是无缝的。
NotExtensible- 公共注释,向用户表明她不应提供给定类型的自定义实现。帮助框架集成商和我们的用户了解如何安全地使用 Mockito API。
您有反馈吗?请在问题 1110 中留下评论。 - 新的集成 API:监听验证开始事件(自 2.11.+ 起)
Spring Boot 等框架集成需要公共 API 来解决双代理用例(问题 1191)。我们添加了:
新的VerificationStartedListener并VerificationStartedEvent 启用框架集成器来替换模拟对象进行验证。主要驱动用例是Spring Boot集成。有关详细信息,请参阅 Javadoc VerificationStartedListener。
新的公共方法MockSettings.verificationStartedListeners(VerificationStartedListener…) 允许在模拟创建时提供验证启动的监听器。
添加了新的便捷方法MockingDetails.getMock(),使MockingDetailsAPI 更加完整。我们发现此方法在实施过程中非常有用。 - 新的集成 API:MockitoSession可供测试框架使用(自 2.15.+ 起) 链接图标
MockitoSessionBuilder并MockitoSession通过测试框架集成(例如 JUnit)进行了增强以实现重用MockitoRule:
MockitoSessionBuilder.initMocks(Object…)允许传入多个测试类实例,以初始化使用 Mockito 注释注释的字段,例如Mock。当测试使用多个(例如嵌套的)测试类实例时,此方法对于高级框架集成(例如 JUnit Jupiter)很有用。
MockitoSessionBuilder.name(String)允许从测试框架传递一个名称到 MockitoSession用于在Strictness.WARN使用时打印警告。
MockitoSessionBuilder.logger(MockitoSessionLogger)可以自定义用于在完成模拟时产生的提示/警告的记录器(对于测试和连接测试框架(如 JUnit Jupiter)提供的报告功能很有用)。
MockitoSession.setStrictness(Strictness)允许改变MockitoSession 一次性场景的严格性,例如,它可以为类中的所有测试配置默认严格性,但可以更改单个或几个测试的严格性。
MockitoSession.finishMocking(Throwable)是为了避免由于存在多个竞争故障而可能产生的混淆而添加的。当提供的故障 不是时,它将禁用某些检查null。
44. 已弃用org.mockito.plugins.InstantiatorProvider,因为它泄漏了内部 API。它被替换为org.mockito.plugins.InstantiatorProvider2 (Since 2.15.4) 链接图标
org.mockito.plugins.InstantiatorProvider 返回了一个内部 API。因此,它已被弃用并被 取代InstantiatorProvider2。org.mockito.plugins.InstantiatorProvider 现已被删除。
45.新的 JUnit Jupiter (JUnit5+) 扩展
要与 JUnit Jupiter (JUnit5+) 集成,请使用 org.mockito:mockito-junit-jupiter
工件。有关集成用法的更多信息,请参阅的 JavaDocMockitoExtension。
46. 新功能Mockito.lenient()和MockSettings.lenient()方法 (自 2.20.0 起) 链接图标
自早期的 Mockito 2 开始,就提供了严格的存根功能。它非常有用,因为它可以推动更清晰的测试并提高生产率。严格的存根会报告不必要的存根,检测存根参数不匹配并使测试更加 DRY(Strictness.STRICT_STUBS)。这需要权衡:在某些情况下,您可能会从严格的存根中得到假阴性。为了解决这些问题,您现在可以将特定存根配置为宽松的,而所有其他存根和模拟都使用严格的存根:
lenient().when(mock.foo()).thenReturn(“ok”);
如果您希望给定模拟上的所有存根都宽松,则可以相应地配置模拟:
Foo mock = Mockito.mock(Foo.class, withSettings().lenient());
更多信息请参阅lenient()。通过打开 GitHub 问题进行讨论,让我们知道您如何发现新功能!
47.用于清除内联模拟中的模拟状态的新 API(自 2.25.0 起)
在某些特定的罕见情况下(问题#1619),内联模拟会导致内存泄漏。没有干净的方法可以完全缓解此问题。因此,我们引入了一个新的 API 来明确清除模拟状态(仅在内联模拟中有意义!)。请参阅中的示例用法MockitoFramework.clearInlineMocks()。如果您有反馈或更好的解决问题的想法,请联系我们。
48.模拟静态方法(自 3.4.0 起)
使用内联模拟生成器时,可以模拟当前线程和用户定义范围内的静态方法调用。这样,Mockito 可确保并发和顺序运行的测试不会相互干扰。为确保静态模拟保持临时性,建议在 try-with-resources 构造中定义范围。在以下示例中,类型Foo的静态方法将返回,foo除非被模拟:
assertEquals("foo", Foo.method());try (MockedStatic mocked = mockStatic(Foo.class)) {mocked.when(Foo::method).thenReturn("bar");assertEquals("bar", Foo.method());mocked.verify(Foo::method);}assertEquals("foo", Foo.method());
由于静态模拟的范围已定义,一旦范围被释放,它将返回其原始行为。要定义模拟行为并验证静态方法调用,请使用MockedStatic返回的。
49.模拟对象构造(自 3.5.0 起)
使用内联模拟生成器时,可以在当前线程和用户定义范围内对构造函数调用生成模拟。这样,Mockito 可确保并发和顺序运行的测试不会相互干扰。为确保构造函数模拟保持临时性,建议在 try-with-resources 构造中定义范围。在以下示例中,类型Foo的构造将生成模拟:
assertEquals("foo", new Foo().method());try (MockedConstruction mocked = mockConstruction(Foo.class)) {Foo foo = new Foo();when(foo.method()).thenReturn("bar");assertEquals("bar", foo.method());verify(foo).method();}assertEquals("foo", new Foo().method());
由于模拟构造的范围已定义,一旦范围被释放,对象构造将返回其原始行为。要定义模拟行为并验证方法调用,请使用MockedConstruction返回的。
50.避免在仅模拟接口时生成代码(自 3.12.2 起) 链接图标
JVM 提供了Proxy创建接口类型动态代理的功能。对于大多数应用程序,Mockito 必须能够模拟默认模拟生成器支持的类,甚至模拟内联模拟生成器支持的最终类。要创建此类模拟,Mockito 需要设置不同的 JVM 功能,并且必须应用代码生成。如果只模拟接口,则可以选择使用基于 API 的 org.mockito.internal.creation.proxy.ProxyMockMaker,Proxy 这可以避免其他模拟生成器的多种开销,但也将模拟限制在接口上。可以通过 mockito 扩展机制明确激活此模拟生成器,只需在类路径中创建一个 /mockito-extensions/org.mockito.plugins.MockMaker包含值的文件即可mock-maker-proxy。
51.将类标记为不可模拟(自 4.1.0 起) 链接图标
在某些情况下,模拟类/接口可能会导致意外的运行时行为。例如,java.util.List 考虑到接口所施加的要求,模拟很困难。这意味着在运行时,根据应用程序在列表中调用的方法,您的模拟可能会以违反接口的方式运行。
对于您拥有的任何难以模拟的类/接口,您现在可以使用 标记该类@DoNotMock。有关注释的使用以及如何发送您自己的注释(以避免对测试工件的编译时依赖),请参阅其 JavaDoc。
- @Mock 注释和方法 的新严格性属性MockSettings.strictness()(自 4.6.0 起)
现在,您可以使用@Mock
注释严格性属性或使用MockSettings.strictness()
自定义单个模拟的严格性级别。如果您希望所有模拟都严格,但其中一个模拟宽松,那么这将非常有用。
@Mock(strictness = Strictness.LENIENT)Foo mock;// using MockSettings.withSettings()Foo mock = Mockito.mock(Foo.class, withSettings().strictness(Strictness.WARN));
- 为单个模拟指定模拟制作者(自 4.8.0 起)
您可能会遇到只想对特定测试使用不同的模拟生成器的情况。在这种情况下,您可以(暂时)使用MockSettings.mockMaker(String)和Mock.mockMaker() 为导致问题的特定模拟指定模拟生成器。
// using annotation@Mock(mockMaker = MockMakers.SUBCLASS)Foo mock;// using MockSettings.withSettings()Foo mock = Mockito.mock(Foo.class, withSettings().mockMaker(MockMakers.SUBCLASS));
- 不指定类别的模拟/监视(自 4.10.0 起)
现在,您可以调用不带参数的方法或方法,而不是mock(Class)使用Classspy(Class)参数: mock()spy()
Foo foo = Mockito.mock();Bar bar = Mockito.spy();
Mockito 将自动检测所需的类。
仅当您将结果分配给具有显式类型的变量或字段时,它才有效mock()。spy()对于隐式类型,Java 编译器无法自动确定模拟的类型,您需要Class显式传入。
- 使用断言进行验证(自 5.3.0 起) 链接图标
ArgumentCaptor为了在验证过程中验证参数,现在可以使用 }, 而不是使用 捕获它们ArgumentMatchers.assertArg(Consumer):
verify(serviceMock).doStuff(assertArg(param -> {assertThat(param.getField1()).isEqualTo("foo");assertThat(param.getField2()).isEqualTo("bar");}));