Mockito验证

本文是我们名为“ 用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错误的快捷方式。
  • InvocationMatcherInvocationsMarker结合使用以查找所需的调用(如果发生)。

我们将创建一个称为FirstVerificationMode ,它将验证给定方法是否是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) {List<Invocation> invocations = data.getAllInvocations();InvocationMatcher matcher = data.getWanted();List<Invocation> 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 List<String> 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);}
}

我们将使用模拟PrinterArgumentCaptor创建一个新的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 ArgumentCaptor<String> textCaptor;@Beforepublic void setUp() throws Exception {diagnostics = new PrinterDiagnostics(printer);}
}

在这里,我们看到我们创建了一个被测类的实例,诊断,一个代表打印机,打印机的Mock,以及一个String参数的ArgumentCaptor,以捕获输入到打印机的称为textCaptor的文本。 您可以看到我们用@Captor批注为ArgumentCaptor批注。 因为我们使用了注释,所以Mockito将自动为我们实例化ArgumentCaptor。

您还可以看到ArgumentCaptor是通用类型,在这种情况下,我们将使用Type Argument String创建一个ArgumentCaptor,因为我们将捕获文本参数(即String)。 如果要捕获collat​​e参数,则可能已经创建了ArgumentCaptor collateCaptor ArgumentCaptor collateCaptor

在我们的@Before方法中,我们只需创建一个新的PrinterDiagnostics 。,即可通过其构造函数注入模拟打印机。

现在让我们创建测试。 我们要确保两件事:

1.份数被添加到输入文本中。
2. collat​​e参数的状态已添加到输入文本。
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()匹配器来确保我们通过预期的副本并整理参数。

如果对嘲笑的方法进行了多次调用,我们可以使用ArgumentCaptorgetValues()方法获取所有字符串的列表,这些字符串作为每次调用中的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));List<String> 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/355928.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

js页面传值(注意中文)

页面a&#xff08;传送&#xff09;&#xff1a; var myUrlhttp://www.xxx?myParaencodeURI(中文参数);页面b&#xff08;接收&#xff0c;注意中文&#xff09;: //var myPara request("myPara"); function request(paras){// var url location.href;var urldeco…

stax 和jaxb 关系_XML解组基准:JAXB,STAx,Woodstox

stax 和jaxb 关系介绍 上周末&#xff0c;我开始考虑如何以一种资源友好的方式处理大量XML数据。我要解决的主要问题是如何以块的形式处理大型XML文件&#xff0c;同时提供上游/下游系统&#xff0c;需要处理一些数据。 当然&#xff0c;我已经使用JAXB技术已有几年了。 使用J…

C语言switch怎么算,超级新手,用switch写了个计算器程序,求指导

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼我也写过一个。。。。看样子大家都喜欢用 switch()来写计算机。。。。百度排版是渣渣&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;# includeintmain(void){ doublea, b, c;//a: 被X数&#xff0c; b: X数&#x…

vim编辑器初级(七)

:syntax on  打开语法高亮 :syntax off  关闭语法高亮 vim有两套语法颜色方案&#xff0c;分别用于背景亮和背景暗时&#xff0c;当vim启动时它会尝试辨别背景并选择合适的颜色方案&#xff0c;但vim辨别背景时可能出错 :set background?  查看background选项的值为多少…

如何编写Java代理

对于vmlens &#xff08;轻量级Java竞争条件捕获器&#xff09;&#xff0c;我们使用Java代理来跟踪字段访问。 这是我们学习的实现此类代理的经验教训。 开始 使用“ static public static void premain&#xff08;String args&#xff0c;Instrumentation inst&#xff09;…

48道C语言上机题参考答案,二级C语言上机题库参考答案(已修改).doc

二级C语言上机题库参考答案(已修改).doc下载提示(请认真阅读)1.请仔细阅读文档&#xff0c;确保文档完整性&#xff0c;对于不预览、不比对内容而直接下载带来的问题本站不予受理。2.下载的文档&#xff0c;不会出现我们的网址水印。3、该文档所得收入(下载内容预览)归上传者、…

Python练习-从小就背不下来的99乘法表

心血来潮,灵机一动,反正就是无聊的做了一个很简单的小玩意: for i in range(1,10):#让i 1-9 循环9次print("\n")#每循环一次进行一次换行for j in range(1,10):#让j 1-9 循环9次print(j, "x",i , "", i * j," ",end"")#输出…

快速排序 数组 函数 c语言,书上快速排序程序中子函数是怎样更改数组的?理论上不可实现啊...

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼return不可能返回数组&#xff0c;于是子hanshu1quicksort最后没有用return&#xff0c;但是没有return又怎么可以更改a[N]&#xff1f;主函数中的quicksort(a, 0, N - 1);将a[N]复制后传递给形参a&#xff0c;quicksort(a, 0, N -…

(原创)SpringBoot入门

本文章是SpringBoot入门的介绍在这里 我会尽量写一些细节性的东西,我用的是IDEA2016 Tomcat7 JDK1.8 Maven3.3.9 IDEA Tomcat JDK Maven的安装我就不详细了, 这里我会提到Maven的安装和如何使用阿里云的镜像,因为官网的JAR下载真的是...比乌龟还慢 先从Maven的配置开始,到官…

jbox2d_JBox2D和JavaFX:事件与力量

jbox2d在昨天的示例中&#xff0c;您看到了如何创建一个简单的世界并使用WorldView进行显示&#xff0c;以及如何提供自定义渲染器。 现在&#xff0c;我们将添加一些用户输入。 我们将创建一个类似于弹球机中的鳍状肢的控件。 为此&#xff0c;我们将创建一个关节。 在JBox2D中…

整数因子分解c语言递归,整数因子分解:计算一个整数所有的分解式(递归实现)...

原始问题描述&#xff1a;对于给定的正整数n&#xff0c;计算n有多少种不同的分解式。例如&#xff0c;当n12时&#xff0c;有8种不同的分解式&#xff1a;1212,1262,1243,1234,12322,1226,12232 ,12223对n的每个因子递归搜索&#xff0c;代码如下&#xff1a;void solve (int …

Tomcat 部署了web项目中文乱码

本地Tomcat 启动&#xff0c;网页端的中文传到后台显示乱码。 查询前段已经设置了 UTF-8格式&#xff0c; 后台收到中文的依然是乱码&#xff0c;原来是Tomcat的配置问题。 修改Tomcat的配置文件server.xml &#xff0c;添加一个属性 URIEncoding"UTF-8"&#xff0c;…

电脑基础c语言,C语言经验: 如何从零基础学习C语言?

C语言是面向过程的&#xff0c;而C 是面向对象的C和C 之间的区别:C是一种结构化语言&#xff0c;其重点是算法和数据结构. 在C程序设计中&#xff0c;首先要考虑的是如何通过过程来处理输入(或环境条件)以获得输出(或实现过程(事务)控制).C &#xff0c;首先要考虑的是如何构建…

Android布局动态化,一种基于堆积木思想的卡片式Android动态布局方法与流程

本发明涉及android界面布局&#xff0c;特别是一种基于堆积木思想的卡片式Android动态布局方法。背景技术&#xff1a;在Android流行的今天&#xff0c;android App在生活中使用日益广泛&#xff0c;相对PC&#xff0c;手机由于界面的尺寸的限制&#xff0c;手机的UI设计和布局…

java cpu_Java High CPU故障排除指南–第1部分

java cpu本文是该系列的第1部分&#xff0c;它将为您提供有关如何进行故障排除和识别Java高CPU问题根本原因的综合指南。 本指南也适用于独立的Java程序&#xff0c;但旨在帮助涉及Java EE企业日常生产支持的个人。 它还将包括最常见的高级CPU问题列表以及高级解决方案。 生产…

android:ellipsize=end 不起作用,android:ellipsize=end 失效或者 相关的Bug

其实这文章有点傻逼。相关的问题TextView android:ellipsize“end”超出一个字符时不显示…的解决http://www.pocketdigi.com/20140122/1261.html上面我到没有遇到过。但是我遇到的更神奇。就是在使用android:ellipsize“end”正常的情况下这个属性。该textView的文本就不能加入…

适用于具有Couchbase和WildFly的多容器和多主机应用程序的Docker Machine,Swarm和Compose...

该博客将说明如何使用Docker创建部署在多个主机上的多容器应用程序。 这将使用Docker Machine&#xff0c;Swarm和Compose实现。 是的&#xff0c;所有这三个工具一起使此博客更加有趣&#xff01; 该图说明了关键组件&#xff1a; Docker Machine用于配置多个Docker主机 …

android 手机工具箱,Android工具箱下载

安卓工具箱专业版是款集所有功能于一身的工具箱&#xff01;包括硬件和软件和工具&#xff0c;您的手机使用的信息。非常容易使用&#xff0c;具有非常用户友好的用户界面。 主要功能&#xff1a; 1。硬件信息&#xff1a;CPU核心&#xff0c;CPU类型&#xff0c;内存信息&…

日记

时间&#xff1a;18年4月15日下午将近2点 地点&#xff1a;北京昌平区 本来是在github上写技术博客。突然听到楼下小朋友的嘻戏声&#xff0c;也是不止一次让这种声音勾起了我的回忆。 这种声音就仿佛是自己小时候和小朋友玩一般&#xff0c;但是&#xff0c;我现在却是在北京。…

一键复制android代码,兼容安卓和ios实现一键复制内容到剪切板

js兼容安卓和ios实现粘贴板一键复制color: #000;background: #fff;overflow-y: scroll;-webkit-text-size-adjust: 100%;-ms-text-size-adjust: 100%;}html*{outline:0;-webkit-text-size-adjust: none;-webkit-tap-highlight-color: transparent}*{margin:0;padding:0}.conten…