国外设计网站dooor,石家庄房产网,发布友情链接,企业wordpress主题本文是我们名为“ 用Mockito进行测试 ”的学院课程的一部分。 在本课程中#xff0c;您将深入了解Mockito的魔力。 您将了解有关“模拟”#xff0c;“间谍”和“部分模拟”的信息#xff0c;以及它们相应的Stubbing行为。 您还将看到使用测试双打和对象匹配器进行验证的过… 本文是我们名为“ 用Mockito进行测试 ”的学院课程的一部分。 在本课程中您将深入了解Mockito的魔力。 您将了解有关“模拟”“间谍”和“部分模拟”的信息以及它们相应的Stubbing行为。 您还将看到使用测试双打和对象匹配器进行验证的过程。 最后讨论了使用Mockito的测试驱动开发TDD以了解该库如何适合TDD的概念。 在这里查看 目录 1.什么是验证 2.使用verify 2.1。 使用内置的验证模式 2.2。 创建自定义验证模式 2.3。 参数验证 2.4。 超时验证 3.验证无交互且无更多交互 4.按顺序验证 5.争论者 六结论 7.下载源代码 1.什么是验证 验证是确认模拟行为的过程。 这对于确定我们正在测试的类是否已按预期方式与其任何依赖项进行交互非常有用。 有时我们对从Mock返回的值不感兴趣但是对被测类如何与之交互发送什么值或调用它的频率感兴趣。 确认此行为的过程是验证Mockito提供了许多工具来允许我们执行此操作。 2.使用verify Mockito工具箱中用于执行验证的主要工具是org.mockito.Mockito.verify()方法。 verify方法将Mock对象作为参数并返回与Mock相同的Class的实例从而允许您调用Class的方法Mockito将该类的方法解释为验证该方法是否存在某种交互的请求。 让我们再次看一下上一教程中的打印机界面。 public interface Printer {void printTestPage();} 我们可以创建一个简单的单元测试来演示使用模拟打印机进行验证 import static org.mockito.Mockito.verify;import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;RunWith(MockitoJUnitRunner.class)
public class PrinterTest {Mockprivate Printer printer;Testpublic void simple_interaction_verification() {// Given// Whenprinter.printTestPage();// Thenverify(printer).printTestPage(); }
} 我们可以看到我们的单元测试首先调用printer.printTestPage() 。 这是在模拟被测类中的可能交互但是为了简单起见我们在单元测试类中进行了模拟。 下一个调用是对verify(printer).printTestPage()的调用。 这指示Mockito检查是否对Mock打印机的printTestPage()方法进行了一次调用。 请仔细注意调用的语法 verify()的参数是Mock对象而不是方法调用。 如果我们放置了verify(printer.printTestPage())我们将产生一个编译错误。 将其与存根中给定的/当语法相比较语法形式为when(mockObject.someMethod()).thenReturn(...) 。 如果我们没有在此调用上方调用printTestPage()来验证Mockito会产生验证错误则通知我们没有调用printTestPage() 如下所示 Wanted but not invoked:
printer.printTestPage();
- at com.javacodegeeks.hughwphamill.mockito.stubbing.PrinterTest.simple_interaction_verification(PrinterTest.java:24)
Actually, there were zero interactions with this mock. 另外如果我们再次调用printTestPage() Mockito会产生一个验证错误通知我们printTestPage()调用过多。 该错误如下所示 org.mockito.exceptions.verification.TooManyActualInvocations:
printer.printTestPage();
Wanted 1 time:
- at com.javacodegeeks.hughwphamill.mockito.stubbing.PrinterTest.simple_interaction_verification(PrinterTest.java:25)
But was 2 times. Undesired invocation:
- at com.javacodegeeks.hughwphamill.mockito.stubbing.PrinterTest.simple_interaction_verification(PrinterTest.java:22) 有用的是错误告诉我们哪一行代码包含了多余的调用在本例中为PrinterTest.java第22行。 但是如果我们想与我们的Mock进行多次互动该怎么办 Mockito支持吗 毫不奇怪答案是肯定的 verify()方法采用第二个类型为org.mockito.verification.VerificationMode参数该参数可用于提供有关与模拟的所需交互的其他详细信息。 使用内置的验证模式 像往常一样Mockito在org.mockito.Mockito中提供了许多方便的静态方法来创建VerificationModes例如 times(int) 这将验证该方法被调用次数。 Test
public void simple_interaction_verification_times_1() {// Given// Whenprinter.printTestPage();// Thenverify(printer, times(1)).printTestPage();
} 请注意 verify(mock)是verify(mock, times(1))的别名。 当然我们可以使用times()验证多个交互 Test
public void simple_interaction_verification_times_3() {// Given// Whenprinter.printTestPage();printer.printTestPage();printer.printTestPage();// Thenverify(printer, times(3)).printTestPage();
} 当实际的调用次数与预期的次数不匹配时此VerificationMode将产生有用的错误。 调用不足 org.mockito.exceptions.verification.TooLittleActualInvocations:
printer.printTestPage();
Wanted 3 times:
- at com.javacodegeeks.hughwphamill.mockito.stubbing.PrinterTest.simple_interaction_verification_times_3(PrinterTest.java:49)
But was 2 times:
- at com.javacodegeeks.hughwphamill.mockito.stubbing.PrinterTest.simple_interaction_verification_times_3(PrinterTest.java:45) 调用过多 org.mockito.exceptions.verification.TooManyActualInvocations:
printer.printTestPage();
Wanted 3 times:
- at com.javacodegeeks.hughwphamill.mockito.stubbing.PrinterTest.simple_interaction_verification_times_3(PrinterTest.java:50)
But was 4 times. Undesired invocation:
- at com.javacodegeeks.hughwphamill.mockito.stubbing.PrinterTest.simple_interaction_verification_times_3(PrinterTest.java:47) atLeastOnce() atLeast(int) 这将验证该方法至少被调用了给定的次数。 Test
public void simple_interaction_verification_atleastonce() {// Given// Whenprinter.printTestPage();printer.printTestPage();// Thenverify(printer, atLeastOnce()).printTestPage();
}Test
public void simple_interaction_verification_atleast_2() {// Given// Whenprinter.printTestPage();printer.printTestPage();printer.printTestPage();// Thenverify(printer, atLeast(2)).printTestPage();
} 像往常一样我们得到全面的错误报告 org.mockito.exceptions.verification.TooLittleActualInvocations:
printer.printTestPage();
Wanted *at least* 2 times:
- at com.javacodegeeks.hughwphamill.mockito.stubbing.PrinterTest.simple_interaction_verification_atleast_2(PrinterTest.java:76)
But was 1 time:
- at com.javacodegeeks.hughwphamill.mockito.stubbing.PrinterTest.simple_interaction_verification_atleast_2(PrinterTest.java:71) atMost(int) 这将验证该方法最多被调用了给定的次数。 Test
public void simple_interaction_verification_atmost_3() {// Given// Whenprinter.printTestPage();printer.printTestPage();// Thenverify(printer, atMost(3)).printTestPage();
} 和错误情况 org.mockito.exceptions.base.MockitoAssertionError:
Wanted at most 3 times but was 4at com.javacodegeeks.hughwphamill.mockito.stubbing.PrinterTest.simple_interaction_verification_atmost_3(PrinterTest.java:91) never() 这将验证未调用该方法。 Test
public void simple_interaction_verification_never() {// Given// When// Thenverify(printer, never()).printTestPage();
} 和错误情况 org.mockito.exceptions.verification.NeverWantedButInvoked:
printer.printTestPage();
Never wanted here:
- at com.javacodegeeks.hughwphamill.mockito.stubbing.PrinterTest.simple_interaction_verification_never(PrinterTest.java:102)
But invoked here:
- at com.javacodegeeks.hughwphamill.mockito.stubbing.PrinterTest.simple_interaction_verification_never(PrinterTest.java:98) only() 这将验证所验证的方法是调用的唯一方法。 Test
public void simple_interaction_verification_only() {// Given// Whenprinter.printTestPage();// Thenverify(printer, only()).printTestPage();
} 我们可以通过将以下方法添加到打印机界面来产生错误 void turnOff(); 并在我们的测试中调用它 Test
public void simple_interaction_verification_only_fails() {// Given// Whenprinter.printTestPage();printer.turnOff();// Thenverify(printer, only()).printTestPage();
} 给出以下错误 org.mockito.exceptions.verification.NoInteractionsWanted:
No interactions wanted here:
- at com.javacodegeeks.hughwphamill.mockito.stubbing.PrinterTest.simple_interaction_verification_only_fails(PrinterTest.java:124)
But found this interaction:
- at com.javacodegeeks.hughwphamill.mockito.stubbing.PrinterTest.simple_interaction_verification_only_fails(PrinterTest.java:120)
***
For your reference, here is the list of all invocations ([?] - means unverified).
1. [?]- at com.javacodegeeks.hughwphamill.mockito.stubbing.PrinterTest.simple_interaction_verification_only_fails(PrinterTest.java:120)
2. [?]- at com.javacodegeeks.hughwphamill.mockito.stubbing.PrinterTest.simple_interaction_verification_only_fails(PrinterTest.java:121)创建自定义验证模式 您可以通过实现org.mockito.verification.VerificationMode接口来创建自己的自定义验证模式。 请注意这使用的某些类不构成Mockito的公共API的一部分。 在撰写本文时已经计划将它们提升为公共API但是在实施发生更改之前应谨慎使用此功能。 VerificationMode公开了一个void verify(VerificationData data)方法该方法用于验证我们感兴趣的模拟调用是否正确发生。 您可以使用几个内部Mockito类在VerificationMode中为您提供帮助 InvocationsFinder将返回带有感兴趣的模拟的所有调用的列表。 InvocationMarker可用于将模拟调用标记为已验证。 Reporter公开了许多引发各种VerificationFailure错误的快捷方式。 InvocationMatcher与InvocationsMarker结合使用以查找所需的调用如果发生。 我们将创建一个称为First的VerificationMode 它将验证给定方法是否是Mock上的首次调用。 我们将创建一个实现VerificationMode的类并在verify方法中找到所有匹配的调用并验证两件事 1.我们想要的调用实际上发生了如果没有发生我们将使用Reporter引发“需要但未调用”的错误。 2.我们想要的调用是在Mock上的第一次调用如果不是我们将抛出一个新的异常并带有适当的消息详细说明预期的调用和实际的调用。 最后我们将通过静态工厂方法公开First的创建以与Mockito语法保持一致。 First类如下所示 package com.javacodegeeks.hughwphamill.mockito.verification;import java.util.Arrays;
import java.util.List;import org.mockito.exceptions.Reporter;
import org.mockito.exceptions.verification.VerificationInOrderFailure;
import org.mockito.internal.debugging.LocationImpl;
import org.mockito.internal.invocation.InvocationMarker;
import org.mockito.internal.invocation.InvocationMatcher;
import org.mockito.internal.invocation.InvocationsFinder;
import org.mockito.internal.verification.api.VerificationData;
import org.mockito.invocation.Invocation;
import org.mockito.verification.VerificationMode;public class First implements VerificationMode {private final InvocationsFinder finder new InvocationsFinder();private final InvocationMarker marker new InvocationMarker();private final Reporter reporter new Reporter();public static VerificationMode first() {return new First();}Overridepublic void verify(VerificationData data) {ListInvocation invocations data.getAllInvocations();InvocationMatcher matcher data.getWanted();ListInvocation chunk finder.findInvocations(invocations, matcher);if (invocations.size() 0 || chunk.size() 0) {reporter.wantedButNotInvoked(matcher);} else if (!sameInvocation(invocations.get(0), chunk.get(0))) { reportNotFirst(chunk.get(0), invocations.get(0));}marker.markVerified(chunk.get(0), matcher); }private boolean sameInvocation(Invocation left, Invocation right) {if (left right) {return true; } return left.getMock().equals(right.getMock()) left.getMethod().equals(right.getMethod()) Arrays.equals(left.getArguments(), right.getArguments());}private void reportNotFirst(Invocation wanted, Invocation unwanted) {StringBuilder message new StringBuilder();message.append(\\nWanted first:\\n).append(wanted).append(\\n).append(new LocationImpl());message.append(\\nInstead got:\\n).append(unwanted).append(\\n).append(unwanted.getLocation()).append(\\n);throw new VerificationInOrderFailure(message.toString());}
} 我们可以在这样的测试案例中使用它 Test
public void simple_interaction_verification_first() {// Given// Whenprinter.printTestPage();printer.turnOff(); // Thenverify(printer, first()).printTestPage();
} 或捕获一些意外行为 Test
public void simple_interaction_verification_first_fails() {// Given// Whenprinter.turnOff();printer.printTestPage(); // Thenverify(printer, first()).printTestPage();
} 会产生以下错误 org.mockito.exceptions.verification.VerificationInOrderFailure:
Wanted first:
printer.printTestPage();
- at com.javacodegeeks.hughwphamill.mockito.verification.PrinterTest.simple_interaction_verification_first_fails(PrinterTest.java:152)
Instead got:
printer.turnOff();
- at com.javacodegeeks.hughwphamill.mockito.verification.PrinterTest.simple_interaction_verification_first_fails(PrinterTest.java:148)参数验证 我们将检查带有参数的方法的验证因此让我们更新Printer接口以添加新方法。 此方法将模拟打印文本字符串并将包含以下参数 字符串文本–要打印的文本。 整数副本–要制作的副本数量。 布尔整理–整理副本为True。 public interface Printer {void printTestPage();void turnOff();void print(String text, Integer copies, Boolean collate);} 带参数的验证使我们不仅可以验证是否与Mock有交互还可以验证将哪些参数传递给了Mock。 要使用参数执行验证您只需在Mock上的verify调用中将感兴趣的参数传递到Mocked方法中即可。 Test
public void verificatin_with_actual_parameters() {// GivenString text Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.;Integer copies 3;Boolean collate true;// Whenprinter.print(text, copies, collate);// Thenverify(printer).print(text, copies, collate);
} 再次仔细注意verify()的语法我们在对verify方法返回的对象上调用print() 而不是直接在Mock上。 您可以看到只需将值传递给print()就足以使用参数执行验证。 以下测试将失败 Test
public void verificatin_with_actual_parameters_fails() {// GivenString text Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.;String text2 Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.;Integer copies 3;Boolean collate true;// Whenprinter.print(text2, copies, collate);// Thenverify(printer).print(text, copies, collate);
} 具有以下输出 Argument(s) are different! Wanted:
printer.print(Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.,3,true
);
- at com.javacodegeeks.hughwphamill.mockito.verification.PrinterTest.verificatin_with_actual_parameters_fails(PrinterTest.java:185)
Actual invocation has different arguments:
printer.print(Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.,3,true
); 您可以看到Mockito在错误跟踪中为您提供了预期的参数和实际的参数从而非常容易调试失败的测试。 与简单验证一样在使用Parameters时我们可以使用VerificationMode进行更具体的验证。 关键的区别在于我们指定的VerificationMode仅适用于具有指定参数的调用。 因此例如如果我们使用验证模式never() 则说明该方法永远不会使用指定的参数调用而不是根本不会调用。 通过以下测试是因为即使调用了print()方法也绝不会使用指定的参数来调用它。 Test
public void verification_with_actual_parameters_and_verification_mode() {// GivenString text Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.;String text2 Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.;Integer copies 3;Boolean collate true;// Whenprinter.print(text, copies, collate);// Thenverify(printer, never()).print(text2, copies, collate);
} 当我们对我们的多次调用Mock 我们可以验证每一个单独使用多次调用verify Test
public void multiple_verification_with_actual_parameters() {// GivenString text Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.;String text2 Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.;Integer copies 3;Boolean collate true;// Whenprinter.print(text, copies, collate);printer.print(text2, copies, collate);// Thenverify(printer).print(text, copies, collate);verify(printer).print(text2, copies, collate);
} 在很多情况下我们对交互的实际参数不感兴趣或不清楚就像在存根阶段一样我们可以使用参数匹配器来验证交互。 看下面的测试 Test
public void verification_with_matchers() {// GivenString text Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.;String text2 Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.;Integer copies3 3;Integer copies4 4;Boolean doCollate true;Boolean doNotCollate false;// Whenprinter.print(text, copies3, doCollate);printer.print(text2, copies4, doNotCollate);// Thenverify(printer, times(2)).print(anyString(), anyInt(), anyBoolean());
} 请注意我们两次调用printer.print() 每次都使用完全不同的参数。 我们使用参数匹配器在最后一行验证与模拟的两种交互。 请记住 verify(printer).print()隐式意味着我们要验证与Mock上的print()方法的一个且只有一个交互因此我们必须包括times(2) VerificationMode以确保我们验证与嘲笑。 验证中使用的参数匹配器与存根阶段中使用的参数匹配器相同。 请重新访问“ 存根”教程 以获取更多可用匹配器列表。 与存根阶段一样我们无法将实参匹配器与真实值混合和匹配但是如果我们不在乎传递给打印机以进行打印的文本我们只想验证应该有5份整理后的副本 在这种情况下与Stubbing一样我们可以使用eq()匹配器来验证我们感兴趣的实数值而对文本使用anyString() 。 Test
public void verification_with_mixed_matchers() {// GivenString text Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.;String text2 Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.;Integer copies 5;Boolean collate true;// Whenprinter.print(text, copies, collate);printer.print(text2, copies, collate);// Thenverify(printer, times(2)).print(anyString(), eq(copies), eq(collate));
} 通过而以下测试将失败 Test
public void verification_with_mixed_matchers_fails() {// GivenString text Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.;Integer copies5 5;Integer copies10 10;Boolean collate true;// Whenprinter.print(text, copies10, collate);// Thenverify(printer).print(anyString(), eq(copies5), eq(collate));
}超时验证 有时当测试多线程应用程序时我们希望确保某些模拟交互在给定的超时时间内发生。 Mockito提供了一个timeout()方法来帮助我们实现这一目标。 请注意虽然此功能可用但Mockito文档警告不要使用它 “感觉该功能应该很少使用-找出测试多线程系统的更好方法。” 因此在避免健康警告的情况下让我们看几个示例。 假设我们有一些线程将要执行printTestPage()并且我们想验证这种情况是否在100毫秒内发生。 我们可以使用timeout(100)来实现。 可以将其作为第二个参数传递给verify() 并返回一个VerificationWithTimout它是VerificationMode的扩展。 以下测试演示其用法 Test
public void verification_with_timeout() {// Given// WhenExecutors.newFixedThreadPool(1).execute(() - printer.printTestPage());// Thenverify(printer, timeout(100)).printTestPage();
} 在这里我们使用Executor创建一个新的ExecutorService 它可以执行Runnables。 我们利用Java 8 Lambda表达式来动态构建一个新的Runnable 它将执行对printTestPage()的调用。 然后我们调用verify()传递100ms的超时时间。 我们现在可以看看失败的测试。 这次我们将使用方法引用来生成Runnable这是因为Runnable的主体更加复杂-它引入了200ms的睡眠。 Test
public void verification_with_timeout_fails() throws InterruptedException {// Given// WhenExecutors.newFixedThreadPool(1).execute(this::printTestWithSleep);// Thenverify(printer, timeout(100)).printTestPage();
}private void printTestWithSleep() {try {Thread.sleep(200L);printer.printTestPage();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}
} 测试失败并显示一条简单的“需要但未调用”消息。 还可以使用返回的VerificationWithTimeout公开的方法将VerificationModes添加到timeout() 。 Test
public void verification_with_timeout_with_verification_mode() {// Givenint poolsize 5;// WhenExecutorService service Executors.newFixedThreadPool(poolsize);service.execute(this::printTestWithSleep);service.execute(this::printTestWithSleep);service.execute(this::printTestWithSleep);// Thenverify(printer, timeout(500).times(3)).printTestPage();
} 在这里我们使用带有睡眠的测试来执行printTestPage() 3次我们使用一个ExecutorService 它可以运行5个并行线程因此睡眠是同时发生的从而允许所有3次调用都在500ms的限制内发生。 通过将可用线程数减少到1迫使printTestWithSleep调用顺序执行并超过500ms超时可以使测试失败。 Test
public void verification_with_timeout_with_verification_mode_fails() {// Givenint poolsize 1;// WhenExecutorService service Executors.newFixedThreadPool(poolsize);service.execute(this::printTestWithSleep);service.execute(this::printTestWithSleep);service.execute(this::printTestWithSleep);// Thenverify(printer, timeout(500).times(3)).printTestPage();
} 前两个调用发生在400毫秒内而最后一个调用发生在600毫秒后导致500毫秒超时失败并显示以下输出 org.mockito.exceptions.verification.TooLittleActualInvocations:
printer.printTestPage();
Wanted 3 times:
- at com.javacodegeeks.hughwphamill.mockito.verification.PrinterTest.verification_with_timeout_with_verification_mode_fails(PrinterTest.java:410)
But was 2 times:
- at com.javacodegeeks.hughwphamill.mockito.verification.PrinterTest.printTestWithSleep(PrinterTest.java:376)3.验证无交互且无更多交互 我们已经看到可以使用never() VerificationMode来确保不调用Mock的特定方法但是如何验证Mock上没有任何交互呢 Mockito为我们提供了verifyZeroInteractions()方法。 此方法使用varargs允许我们验证在一行代码中没有与多个模拟进行任何交互。 让我们在测试类中添加其他一些Mock Mock
private ListString list; 现在我们可以编写以下简单测试以验证与打印机或列表之间没有任何交互 Test
public void verify_zero_interactions() {// Given// When// ThenverifyZeroInteractions(printer, list);
} 与往常一样以下测试将失败 Test
public void verify_zero_interactions_fails() {// Given// Whenprinter.printTestPage();// ThenverifyZeroInteractions(printer, list);
} 具有以下输出 org.mockito.exceptions.verification.NoInteractionsWanted:
No interactions wanted here:
- at com.javacodegeeks.hughwphamill.mockito.verification.PrinterTest.verify_zero_interactions_fails(PrinterTest.java:288)
But found this interaction:
- at com.javacodegeeks.hughwphamill.mockito.verification.PrinterTest.verify_zero_interactions_fails(PrinterTest.java:285)
Actually, above is the only interaction with this mock.at com.javacodegeeks.hughwphamill.mockito.verification.PrinterTest.verify_zero_interactions_fails(PrinterTest.java:288) 我们还可以使用verifyNoMoreInteractions()方法来验证一旦验证了一定数量的调用就不再有与Mock的交互。 Test
public void verify_no_more_interactions() {// GivenString text Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.;Integer copies 3;Boolean collate true;// Whenprinter.print(text, copies, collate);// Thenverify(printer).print(text, copies, collate);verifyNoMoreInteractions(printer);
} 您可以在上面看到我们验证了对print()的调用然后验证了与Mock的交互作用。 以下测试将失败因为在对print()的验证调用之后该模拟还存在其他交互 Test
public void verify_no_more_interactions_fails() {// GivenString text Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.;Integer copies 3;Boolean collate true;// Whenprinter.print(text, copies, collate);printer.turnOff();// Thenverify(printer).print(text, copies, collate);verifyNoMoreInteractions(printer);
} 测试失败会生成以下消息 org.mockito.exceptions.verification.NoInteractionsWanted:
No interactions wanted here:
- at com.javacodegeeks.hughwphamill.mockito.verification.PrinterTest.verify_no_more_interactions_fails(PrinterTest.java:342)
But found this interaction:
- at com.javacodegeeks.hughwphamill.mockito.verification.PrinterTest.verify_no_more_interactions_fails(PrinterTest.java:338)
***
For your reference, here is the list of all invocations ([?] - means unverified).
1. - at com.javacodegeeks.hughwphamill.mockito.verification.PrinterTest.verify_no_more_interactions_fails(PrinterTest.java:337)
2. [?]- at com.javacodegeeks.hughwphamill.mockito.verification.PrinterTest.verify_no_more_interactions_fails(PrinterTest.java:338)4.按顺序验证 有时我们想验证与Mocks的交互是否以特定顺序发生。 Mockito提供了一个名为InOrder的类来帮助我们实现这一目标。 我们需要做的第一件事是使用InOrder对象注册要确认调用顺序的模拟程序。 然后我们在Mock对象上执行方法然后为每个要验证其有序执行的模拟方法调用InOrder对象的verify()方法verify()以验证它们发生的顺序。 InOrder.verify()方法的行为几乎与标准的verify()方法类似允许您传入VerificationModes但是无法使用InOrder进行带有超时的验证。 这是按顺序进行验证的示例 Test
public void verify_in_order() {// GivenInOrder inOrder Mockito.inOrder(printer);// Whenprinter.printTestPage();printer.turnOff();// TheninOrder.verify(printer).printTestPage();inOrder.verify(printer).turnOff();
} 相反的失败测试 Test
public void verify_in_order_fails() {// GivenInOrder inOrder Mockito.inOrder(printer);// Whenprinter.turnOff();printer.printTestPage();// TheninOrder.verify(printer).printTestPage();inOrder.verify(printer).turnOff();
} 失败并显示以下错误消息 org.mockito.exceptions.verification.VerificationInOrderFailure:
Verification in order failure
Wanted but not invoked:
printer.turnOff();
- at com.javacodegeeks.hughwphamill.mockito.verification.PrinterTest.verify_in_order_fails(PrinterTest.java:440)
Wanted anywhere AFTER following interaction:
printer.printTestPage();
- at com.javacodegeeks.hughwphamill.mockito.verification.PrinterTest.verify_in_order_fails(PrinterTest.java:436) 您还可以在多个模拟中按顺序验证 Test
public void verify_in_order_multiple() {// GivenInOrder inOrder Mockito.inOrder(printer, list);// Whenprinter.printTestPage();list.clear();printer.turnOff();// TheninOrder.verify(printer).printTestPage();inOrder.verify(list).clear();inOrder.verify(printer).turnOff();
}5.争论者 我们已经研究过使用参数匹配器来验证带有特定参数的调用但是Mockito让我们走得更远捕获传递给调用的参数并直接对它们执行断言。 这对于验证将在传递给协作者的对象上执行的类中的登录非常有用。 执行此操作的工具是一个称为ArgumentCaptor的类和一个名为Captor的注释。 让我们在模型中创建一个名为PrinterDiagnostics的新类。 它将包含一个Printer并公开一个名为diagnosticPrint的方法该方法将具有与Printer.print()相同的参数并将一些诊断信息添加到要打印的文本中。 package com.javacodegeeks.hughwphamill.mockito.verification;public class PrinterDiagnostics {private Printer printer;public PrinterDiagnostics(Printer printer) {this.printer printer;}public void diagnosticPrint(String text, Integer copies, Boolean collate) {StringBuilder diagnostic new StringBuilder();diagnostic.append(** Diagnostic Print **\\n);diagnostic.append(*** Copies: ).append(copies).append( ***\\n);diagnostic.append(*** Collate: ).append(collate).append( ***\\n);diagnostic.append(********************\\n\\n);printer.print(new StringBuilder().append(diagnostic).append(text).toString(), copies, collate);}
} 我们将使用模拟Printer和ArgumentCaptor创建一个新的JUnit测试来测试该类我们将使用它们来验证对打印机的输入。 这是JUnit测试的框架 package com.javacodegeeks.hughwphamill.mockito.verification;import org.junit.Before;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;RunWith(MockitoJUnitRunner.class)
public class PrinterDiagnosticsTest {private PrinterDiagnostics diagnostics;Mockprivate Printer printer;Captorprivate ArgumentCaptorString textCaptor;Beforepublic void setUp() throws Exception {diagnostics new PrinterDiagnostics(printer);}
} 在这里我们看到我们创建了一个被测类的实例诊断一个代表打印机打印机的Mock以及一个String参数的ArgumentCaptor以捕获输入到打印机的称为textCaptor的文本。 您可以看到我们用Captor批注为ArgumentCaptor批注。 因为我们使用了注释所以Mockito将自动为我们实例化ArgumentCaptor。 您还可以看到ArgumentCaptor是通用类型在这种情况下我们将使用Type Argument String创建一个ArgumentCaptor因为我们将捕获文本参数即String。 如果要捕获collate参数则可能已经创建了ArgumentCaptor collateCaptor ArgumentCaptor collateCaptor 。 在我们的Before方法中我们只需创建一个新的PrinterDiagnostics 。即可通过其构造函数注入模拟打印机。 现在让我们创建测试。 我们要确保两件事 1.份数被添加到输入文本中。 2. collate参数的状态已添加到输入文本。 3.保留原始文本。 我们可能还想验证现实世界中的格式和星号但现在让我们满足于验证上述两个条件。 在测试中我们将初始化测试数据执行对diagnosticPrint()的调用然后结合使用verify()和ArgumentCaptor的capture()方法来捕获文本参数。 然后我们将对捕获的String进行必要的断言以通过使用getValue()方法检索捕获的文本来验证我们期望的行为。 Test
public void verify_diagnostic_information_added_to_text() {// GivenString text Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.;Integer copies 3;Boolean collate true;String expectedCopies Copies: copies;String expectedCollate Collate: collate;// Whendiagnostics.diagnosticPrint(text, copies, collate);// Thenverify(printer).print(textCaptor.capture(), eq(copies), eq(collate));assertTrue(textCaptor.getValue().contains(expectedCopies));assertTrue(textCaptor.getValue().contains(expectedCollate));assertTrue(textCaptor.getValue().contains(text));
} 请注意 capture()行为有点类似于Matcher因为您必须对其他参数使用Matchers。 我们使用eq()匹配器来确保我们通过预期的副本并整理参数。 如果对嘲笑的方法进行了多次调用我们可以使用ArgumentCaptor的getValues()方法获取所有字符串的列表这些字符串作为每次调用中的text参数传递。 让我们在PrinterDiagnostics中创建一个新方法该方法将对单个整理的副本以及原始打印进行诊断打印 public void diagnosticAndOriginalPrint(String text, Integer copies, Boolean collate) {diagnosticPrint(text, copies, collate);printer.print(text, copies, collate);
} 现在我们可以使用以下测试方法进行测试 Test
public void verify_diagnostic_information_added_to_text_and_original_print() {// GivenString text Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.;Integer copies 3;Boolean collate true;String expectedCopies Copies: copies;String expectedCollate Collate: collate;// Whendiagnostics.diagnosticAndOriginalPrint(text, copies, collate);// Thenverify(printer, times(2)).print(textCaptor.capture(), eq(copies), eq(collate));ListString texts textCaptor.getAllValues();assertEquals(2, texts.size());// First captured text is Diagnostic PrintassertTrue(texts.get(0).contains(expectedCopies));assertTrue(texts.get(0).contains(expectedCollate));assertTrue(texts.get(0).contains(text));// Second captured text is normal PrintassertFalse(texts.get(1).contains(expectedCopies));assertFalse(texts.get(1).contains(expectedCollate));assertEquals(text, texts.get(1));
} 请注意我们必须在验证中使用times(2) 因为我们希望两次调用print()方法。 当我们的参数是复杂对象或由测试代码创建时ArgumentCaptors特别有用。 您可以轻松捕获参数并对其进行所需的任何类型的验证和确认。 六结论 我们已经详细研究了Mockito的验证阶段。 我们已经研究了开箱即用地验证行为创建自己的验证模式以及使用Argument Captors对数据执行更复杂的断言的方法。 在下一个教程中我们将研究Hamcrest Matcher库如何使我们进一步进行测试验证从而使我们能够进行非常精细的行为验证。 7.下载源代码 这是关于Mockito验证的课程。 您可以在此处下载源代码 mockito3-verification 翻译自: https://www.javacodegeeks.com/2015/11/mockito-verification.html