什么是Mock?

mock测试就是在测试过程中,对于某些不容易构造(如HttpSerlvertRequest就必须在Servlet容器中才能构造出来)或者不容易获取比较复杂的对象(如JDBC中的ResultSet对象),用一个虚拟的对象(Mock对象)来创建以便测试的测试方法。

Mock最大的功能是帮你把单元测试的耦合分解开,如果你的代码对另一个类或者接口有依赖,它能帮你模拟出这些依赖。并帮你验证所调用的依赖的行为。

比如有这样一段依赖代码:

当我们需要测试A类的时候,如果没有Mock。则我们需要把整个依赖树都构建出来(先构建E,再构建D,再构建C,在构建B,最后才能测试A),而使用Mock的话,就可以将结构分解开,像下面这样:

用mock B和mock C的房间,将D,E对象隔离。

Mockito框架

Mockito是Java单元测试Mock框架。灵活而方便。maven配置

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>2.1.0</version>
    <scope>test</scope>
</dependency>

在测试类中静态导入Mockito

import static org.mockito.Mockito.*;

1.Mockito支持使用mock()静态方法创建模拟对象,Mockito可以使用verify()方法来确认某些方法是否被调用过。如下所示:

//mock creation
List mockedList = mock(List.class);

//using mock object
mockedList.add("one");
mockedList.clear();

2.Stubbing表示一次插桩,它的形式是 when(x).thenReturn(y),用于指定mock的行为。Stubbing的示例代码如下:

HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getRemoteHost()).thenReturn("localhost");

我们使用when(…).thenReturn(…)方法链来定义一个行为。例如“when(mockedList.add(“one”)).thenReturn(true)”表示:当调用了mockedList.add(“one”),那么返回true。并且要注意的是,when(…).thenReturn(…)方法链不仅仅要匹配方法的调用,而且要方法的参数一样才能。另外,when(…).thenReturn(…)方法链可以指定多个返回值,当这样做后,如果多次调用指定的方法,那么这个方法会依次返回这些值。例如“when(i.next()).thenReturn(“Hello,”).thenReturn(“Mockito!”)”,这句话表示:第一次调用i.next()时返回“Hello,”,第二次调用i.next()返回“Mockito!”.

3.Mockito还可以指定异常情况,如下所示:

@Test(expected = NoSuchElementException.class)
public void testForIOException() throws Exception {
    Iterator i = mock(Iterator.class);
    when(i.next()).thenReturn("Hello,").thenReturn("Mockito!"); // 1
    String result = i.next() + " " + i.next(); // 2
    Assert.assertEquals("Hello, Mockito!", result);

    doThrow(new NoSuchElementException()).when(i).next(); // 3
    i.next(); // 4
}

上面这个例子中,第3步我们使用了一个新语法:doThrow(ExceptionX).when(x).methodCall, 它的含义是:当调用了x.methodCall方法后,抛出异常ExceptionX.因此doThrow(new NoSuchElementException()).when(i).next() 的含义就是:当第三次调用i.next()后,抛出异常NoSuchElementException。(因为 i 这个迭代器只有两个元素)

4.校验Mock对象方法调用

Mockito会追踪Mock对象的所有方法调用和调用方法时所传递的参数,我们可以通过verify()静态方法来校验指定的方法调用是否满足调用。如下所示:

@Test
public void testVerify() {
    List mockedList = mock(List.class);
    mockedList.add("one");
    mockedList.add("two");
    mockedList.add("three times");
    mockedList.add("three times");
    mockedList.add("three times");
    when(mockedList.size()).thenReturn(5);
    Assert.assertEquals(mockedList.size(), 5);

    verify(mockedList, atLeastOnce()).add("one");//1
    verify(mockedList, times(1)).add("two");//2
    verify(mockedList, times(3)).add("three times");//3
    verify(mockedList, never()).isEmpty();//4
}

上面例子中,有4步校验:

  • 第一句校验 mockedList.add(“one”) 至少被调用了 1 次(atLeastOnce)
  • 第二句校验 mockedList.add(“two”) 被调用了 1 次(times(1))
  • 第三句校验 mockedList.add(“three times”) 被调用了 3 次(times(3))
  • 第四句校验 mockedList.isEmpty() 从未被调用(never)

5.参数匹配器允许灵活地验证或者Stubbing,如下所示:

//stubbing using built-in anyInt() argument matcher
 when(mockedList.get(anyInt())).thenReturn("element");

 //stubbing using custom matcher (let's say isValid() returns your own matcher implementation):
 when(mockedList.contains(argThat(isValid()))).thenReturn("element");

 //following prints "element"
 System.out.println(mockedList.get(999));

 //you can also verify using an argument matcher
 verify(mockedList).get(anyInt());
作者:Jeebiz  创建时间:2019-10-20 23:55
最后编辑:Jeebiz  更新时间:2024-08-04 15:41