mockito验证参数_Mockito验证

mockito验证参数

本文是我们名为“ 用Mockito测试 ”的学院课程的一部分。

在本课程中,您将深入了解Mockito的魔力。 您将了解有关“模拟”,“间谍”和“部分模拟”的信息,以及它们相应的存根行为。 您还将看到使用测试双打和对象匹配器进行验证的过程。 最后,讨论了使用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将该Class的方法解释为验证与该方法是否存在某种交互的请求。

让我们再次看一下上一教程中的打印机界面。

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语法相对应,语法形式为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 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()方法(以我们要验证它们发生的顺序)。

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

mockito验证参数

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

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

相关文章

d3js mysql_D3.js入门指南

近期略有点诸事不顺&#xff0c;趁略有闲余之时&#xff0c;玩起D3.js。之前实际项目中主要是用各种chart如hightchart、echarts等&#xff0c;这些图形库玩起来貌都是完美的&#xff0c;一切皆可配置&#xff0c;但几年前接触了D3之后&#xff0c;觉得前面那chart类库局限的地…

docker 镜像选择_为什么选择Docker?

docker 镜像选择容器并不是什么新鲜事物&#xff0c;但是实现它们总是比需要的要复杂一些。 Docker在简化容器方面取得了长足的进步&#xff0c;并从此引爆了整个世界。 让我们看看为什么。 为什么Docker成为家喻户晓的名字 Docker不是很老。 2014年5月&#xff0c;当我写了一…

layui templet格式化_layui使用templet格式化表格数据的方法

增加js/*---------------------格式化时间开始--------------------------*///对Date的扩展&#xff0c;将 Date 转化为指定格式的String//月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符&#xff0c;//年(y)可以用 1-4 个占位符&#xff0c;毫秒(S)只能用…

设计模式示例_代理设计模式示例

设计模式示例本文是我们名为“ Java设计模式 ”的学院课程的一部分。 在本课程中&#xff0c;您将深入研究大量的设计模式&#xff0c;并了解如何在Java中实现和利用它们。 您将了解模式如此重要的原因&#xff0c;并了解何时以及如何应用模式中的每一个。 在这里查看 &#x…

mysql insert 字符集_有关 MySQL 字符集的注意事项-爱可生

本文关键字&#xff1a;字符集、建库建表一、数据库和字符集1. 建库时指定创建数据库时&#xff0c;显式指定字符集和排序规则&#xff0c;同时&#xff0c;当切换到当前数据库后&#xff0c;参数 character_set_database&#xff0c;collation_database 分别被覆盖为当前显式指…

c#发送讯息_企业讯息

c#发送讯息本文是我们名为“ EAI的Spring集成 ”的学院课程的一部分。 在本课程中&#xff0c;向您介绍了企业应用程序集成模式以及Spring Integration如何解决它们。 接下来&#xff0c;您将深入研究Spring Integration的基础知识&#xff0c;例如通道&#xff0c;转换器和适…

mysql触发器中case语句_一个很好的触发器例子(case when)

CREATE OR REPLACE TRIGGER TR_CGD1BEFORE UPDATE OR INSERT OR DELETE ON BB_MJ_CGD1_TBFOR EACH ROWDECLAREV_COUNT NUMBER;BEGINCASEWHEN UPDATING OR INSERTING THENIF :NEW.DJZT 结束 THEN:NEW.DJZT : 结束;ELSESELECT COUNT(*)INTO V_COUNTFROM BB_MJ_KCRKD2_TB RKD2WHE…

java cr_WildFly 10 CR 2发布– Java EE 7,Java 8,Hibernate 5,JavaScript支持热重载

java cr昨天&#xff0c;WildFly团队发布了最新版本的WildFly 10 。 CR2很可能是预计于十月份发布最终版本之前的最后一个版本。 即使主要支持的Java EE规范是7&#xff0c;WildFly 8和WildFly 9也提供了许多新功能&#xff0c;而WildFly 9和WildFly 9现在制作了三个服务器版本…

python位置参数ppt_如何在Python中使用一个或多个相同的位置参数?

介绍..如果我们正在编写一个对两个数字执行算术运算的程序&#xff0c;则可以将它们定义为两个位置参数。但是由于它们是相同种类的/ python数据类型的参数&#xff0c;因此使用nargs选项告诉argparse您确实需要两种相同的类型可能更有意义。怎么做..1.让我们编写一个程序来减去…

java设计模式教程_Java设计模式教程

java设计模式教程课程大纲 架构和计算机科学中的设计模式是记录特定专业领域中设计问题的解决方案的正式方法。 这个想法是由建筑师Christopher Alexander在建筑领域引入的&#xff0c;并已被修改为包括计算机科学在内的其他各个学科。 设计模式是针对软件设计中给定上下文中常…

python剑指offer面试题_剑指Offer(Python语言)面试题38

面试题38&#xff1a;字符串的排列题目:输入一个字符串&#xff0c;打印出该字符串中字符的所有排列。例如&#xff0c;输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb&#xff0c;bac,bca和cba。# -*- coding:utf-8 -*-class Solution:def Permutation(se…

侬娜·杰尔_杰尔·地狱

侬娜杰尔什么是JAR地狱&#xff1f; &#xff08;或者是classpath地狱&#xff1f;还是依赖地狱&#xff1f;&#xff09;在考虑使用Maven或OSGi等现代开发工具时&#xff0c;哪些方面仍然有意义&#xff1f; 有趣的是&#xff0c;似乎没有对这些问题的结构化答案&#xff08;…

cmake 安装mysql5.6_CMAKE安装MYSQL 5.6.10

mysql5.6.10不支持configure安装了&#xff0c;提供了CMAKE安装方式 #sudo groupadd mysql #sudo useradd mysql -g mysql #sudo mkdir -p /home/mysql/data #sudo mkdir /usr/local/mysql #sudo mkdir /var/log/mysql #sudo chown -R mysql:mysql /home/mysql/data #sudo cho…

java 多线程变量可见性_Java多线程:易变变量,事前关联和内存一致性

java 多线程变量可见性什么是volatile变量&#xff1f; volatile是Java中的关键字。 您不能将其用作变量或方法名称。 期。 我们什么时候应该使用它&#xff1f; 哈哈&#xff0c;对不起&#xff0c;没办法。 当我们在多线程环境中与多个线程共享变量时&#xff0c;通常使用v…

Mysql运行在内核空间_思考mysql内核之初级系列6—innodb文件管理 | 学步园

在上一篇里面&#xff0c;bingxi和alex思考了information_schema&#xff0c;这个一直在innodb外围打转。没有进入到innodb的内部。在后续的文章中&#xff0c;以innodb的为主&#xff0c;逐个思考。Bingxi和alex今天了解了fil文件管理。对应的文件为&#xff1a;D:/mysql-5.1.…

pcl_openmap_OpenMap教程第2部分–使用MapHandler构建基本地图应用程序–第1部分

pcl_openmap1.简介 在第一个教程中&#xff0c;我们创建了一个基本的OpenMap GIS应用程序&#xff0c;该应用程序在JFrame中显示一个从文件系统加载的具有一个形状图层的地图。 该教程基于com.bbn.openmap.app.example.SimpleMap 。 在该教程中&#xff0c;我们使用了以下OpenM…

mysql7.5安装教程_CentOS7.5下yum安装MySQL8图文教程

卸载MariaDB1.列出所有安装的MariaDB rpm 包rpm -qa | grep mariadb2.强制卸载rpm -e --nodeps mariadb-libs-5.5.60-1.el7_5.x86_64安装MySQL1. 环境CentOS7.52. 获取MySQL最新版 rpm包yum仓库下载MySQLyum localinstall https://repo.mysql.com//mysql80-community-release-e…

字符串url获取参数_如何从URL查询字符串获取示例参数或将其附加到URL查询字符串(示例)?...

字符串url获取参数让我们剖析几个简单的用例&#xff0c;并查看视图参数的工作原理&#xff08;视图参数名称不是强制性的&#xff0c;以匹配通过URL查询字符串传递的请求参数&#xff0c;但在本文中&#xff0c;我们将重点讨论这种情况&#xff09;&#xff1a; 情况1 在inde…

mysql和sqlserver分页的区别_关于SQLServer和MySQL 查询分页语句区别

首先来定义几个要用到的参数(例子)t_user数据表int currentPage ; //当前页int pageRecord ; //每页显示记录数关于SqlServer数据库分页SQL语句为:String sql "select top "pageRecord " * from t_user where id not in (select top "(currentPage-1)*pag…

java 微型数据库_Java 9代码工具:使用Java微型基准测试工具的实践会话

java 微型数据库用肉眼看&#xff0c;基准测试似乎只是确定执行某些代码需要花费多长时间的简单问题。 但是&#xff0c;通常情况下&#xff0c;这是幼稚的方法。 提供具有准确和可重复结果的有意义的基准并非易事。 在本文中&#xff0c;我们将向您介绍OpenJDK代码工具项目&a…