5. 参数匹配器 (matchers)
Mockito以自然的java风格来验证参数值: 使用equals()函数。有时,当需要额外的灵活性时你可能需要使用参数匹配器,也就是argument matchers :
// 使用内置的anyInt()参数匹配器 when(mockedList.get(anyInt())).thenReturn("element"); // 使用自定义的参数匹配器( 在isValid()函数中返回你自己的匹配器实现 ) when(mockedList.contains(argThat(isValid()))).thenReturn("element"); // 输出element System.out.println(mockedList.get(999)); // 你也可以验证参数匹配器 verify(mockedList).get(anyInt()); |
常用的参数匹配器:
一些示例代码:
@Test public void testPersonAny(){ when(mPerson.eat(any(String.class))).thenReturn("米饭"); //或: when(mPerson.eat(anyString())).thenReturn("米饭"); //输出米饭 System.out.println(mPerson.eat("面条")); } @Test public void testPersonContains(){ when(mPerson.eat(contains("面"))).thenReturn("面条"); //输出面条 System.out.println(mPerson.eat("面")); } @Test public void testPersonArgThat(){ //自定义输入字符长度为偶数时,输出面条。 when(mPerson.eat(argThat(new ArgumentMatcher<String>() { @Override public boolean matches(String argument) { return argument.length() % 2 == 0; } }))).thenReturn("面条"); //输出面条 System.out.println(mPerson.eat("1234")); } |
需要注意的是,如果你打算使用参数匹配器,那么所有参数都必须由匹配器提供。例如:
verify(mock).someMethod(anyInt(), anyString(), eq("third argument")); // 上述代码是正确的,因为eq()也是一个参数匹配器 verify(mock).someMethod(anyInt(), anyString(), "third argument"); // 上述代码是错误的, 因为所有参数必须由匹配器提供,而参数"third argument"并非由参数匹配器提供,因此会抛出异常 |
像anyObject(), eq()这样的匹配器函数不会返回匹配器。它们会在内部将匹配器记录到一个栈当中,并且返回一个假的值,通常为null。
6. 使用InOrder验证执行执行顺序
验证执行执行顺序主要使用InOrder函数
如,验证mock一个对象的函数执行顺序:
@Test public void testInorder() { List<String> singleMock = mock(List.class); singleMock.add("小明"); singleMock.add("小红"); // 为该mock对象创建一个inOrder对象 InOrder inOrder = inOrder(singleMock); // 验证add函数首先执行的是add("小明"),然后才是add("小红"),否则测试失败 inOrder.verify(singleMock).add("小明"); inOrder.verify(singleMock).add("小红"); } |
验证多个mock对象的函数执行顺序:
@Test public void testInorderMulti() { List<String> firstMock = mock(List.class); List<String> secondMock = mock(List.class); firstMock.add("小明"); secondMock.add("小红"); // 为这两个Mock对象创建inOrder对象 InOrder inOrder = inOrder(firstMock, secondMock); // 验证它们的执行顺序 inOrder.verify(firstMock).add("小明"); inOrder.verify(secondMock).add("小红"); } |
验证执行顺序是非常灵活的,你不需要一个一个的验证所有交互,只需要验证你感兴趣的对象即可。 你可以选择单个mock对象和多个mock对象混合着来,也可以仅通过那些需要验证顺序的mock对象来创建InOrder对象。
7. 使用Spy监控真实对象
监控真实对象使用spy()函数生成,或者也可以像@Mock那样使用@Spy注解来生成一个监控对象, 当你你为真实对象创建一个监控(spy)对象后,在你使用这个spy对象时真实的对象也会也调用,除非它的函数被stub了。尽量少使用spy对象,使用时也需要小心形式。
@Test public void testSpy() { List<String> list = new ArrayList<>(); List<String> spy = spy(list); // 你可以选择为某些函数打桩 when(spy.size()).thenReturn(100); // 调用真实对象的函数 spy.add("one"); spy.add("two"); // 输出第一个元素"one" System.out.println(spy.get(0)); // 因为size()函数被打桩了,因此这里返回的是100 System.out.println(spy.size()); // 验证交互 verify(spy).add("one"); verify(spy).add("two"); } |
使用@Spy生成监控对象:
@Spy Person mSpyPerson; @Test public void testSpyPerson() { //将会输出Person 类中getName()的真实实现,而不是null System.out.println(mSpyPerson.getName()); } |
理解监控真实对象非常重要!有时,在监控对象上使用when(Object)来进行打桩是不可能或者不切实际的。因此,当使用监控对象时请考虑doReturn|Answer|Throw()函数族来进行打桩。例如:
List list = new LinkedList(); List spy = spy(list); // 不可能实现 : 因为当调用spy.get(0)时会调用真实对象的get(0)函数, // 此时会发生IndexOutOfBoundsException异常,因为真实List对象是空的 when(spy.get(0)).thenReturn("foo"); // 你需要使用doReturn()来打桩 doReturn("foo").when(spy).get(0); |
8. 使用ArgumentCaptor进行参数捕获
参数捕获主要为了下一步的断言做准备,示例代码:
@Test public void argumentCaptorTest() { List<Object> mock = mock(List.class); mock.add("John"); //构建要捕获的参数类型,这里是String ArgumentCaptor argument = ArgumentCaptor.forClass(String.class); //在verify方法的参数中调用argument.capture()方法来捕获输入的参数 verify(mock).add(argument.capture()); //验证“John”参数捕获 assertEquals("John", argument.getValue()); } |
@Test public void argumentCaptorTest2() { List<Object> mock = mock(List.class); mock.add("Brian"); mock.add("Jim"); ArgumentCaptor argument = ArgumentCaptor.forClass(String.class); verify(mock, times(2)).add(argument.capture()); //如果又多次参数调用,argument.getValue()捕获到的是最后一次调用的参数 assertEquals("Jim", argument.getValue()); //如果要获取所有的参数值可以调用argument.getAllValues() assertArrayEquals(new Object[]{"Brian","Jim"}, argument.getAllValues().toArray()); } |
9. 使用@InjectMocks自动注入依赖对象
有时我们要测试的对象内部需要依赖另一个对象,例如:
public class User { private Address address; public void setAddress(Address address) { this.address = address; } public String getAddress() { return address.getDetail(); } } |
public class Address { public String getDetail() { return "detail Address"; } } |
User类内部需要依赖Address类,当我们测试的时候需要mock出这两个对象,然后将Address对象传入到User当中,这样如果依赖的对象多了的话就相当麻烦,Mockito 提供了可以不用去手动注入对象的方法,首先使用@InjectMocks注解需要被注入的对象,如User,然后需要被依赖注入的对象使用@Mock或@Spy注解,之后Mockito 会自动完成注入过程,例如:
@InjectMocks User mTestUser; @Mock Address mAddress; @Test public void argumentInjectMock() { when(mAddress.getDetail()).thenReturn("浙江杭州"); System.out.println(mTestUser.getAddress()); } |
这样就不用关心为User 设置Address ,只要为User需要依赖的类添加注解就可以了,然后直接将重点放到测试方法的编写上。
或者使用@Spy监控真实对象注入也可以:
@InjectMocks User mTestUser; @Spy Address mAddress; @Test public void argumentInjectMock() { // when(mAddress.getDetail()).thenReturn("浙江杭州"); System.out.println(mTestUser.getAddress()); } |
其他:
连续调用的另一种更简短的版本:
// 第一次调用时返回"one",第二次返回"two",第三次返回"three" when(mock.someMethod("some arg")).thenReturn("one", "two", "three"); |
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理