安卓单元测试Mockito的使用

Android Mockito 使用目录

简单的例子

Mockito - mock返回值

Mockito - 参数匹配器

Mockito - 验证调用次数

Mockito - 验证顺序

Mockito - 保模与其他mock未发生相互影响

Mockito - 找出冗余的调用

Mockito - 使用@Mock注解

Mockito - 使用迭代器方式验证我们的参数

Mockito - spy (间谍)

Mockito - 重置

Mockito - BDD 测试

Mokito -  Verification with timeout

Mockito - 自定义验证失败消息

Mockito - when 的使用

Mockito - verify的使用

Mockito - 一些其他的API

doNothing

doReturn

inOrder

ignoreStubs

times

never

atLeastOnce

atLeast

atMost

calls

only

timeout

after


Mockito - Android支持

在Mockito 2.6.1版中,提供了“本机” Android支持。要启用Android支持,请将`mockito-android`库作为依赖项添加到我们的项目中。该工件已发布到同一Mockito组织,并且可以如下导入Android:

 repositories {
   jcenter()
 }
 dependencies {
   testImplementation "org.mockito:mockito-core:+"
   androidTestImplementation "org.mockito:mockito-android:+"
 }

我们可以通过在“ testImplementation”范围内使用“ mockito-core”库,继续在常规VM上运行相同的单元测试,如上所示。请注意,由于Android VM中的限制,我们不能在Android上使用Mockito,但是如果我们想要使用的话那么需要倒入“mockito-android”。

简单的例子

 //Let's import Mockito statically so that the code looks clearer
 import static org.mockito.Mockito.*;//mock creation
 
 List mockedList = mock(List.class);
 //using mock object
 mockedList.add("one");
 mockedList.clear();
 //verification List add() method called and param is "one"
 verify(mockedList).add("one");
 //verification List clear() method called 
 verify(mockedList).clear();

一旦创建,模拟程序将会记住所有的交互。然后我们就可以有选择的验证我们感兴趣的任何事情。

Mockito - mock返回值


//You can mock concrete classes, not just interfaces
 LinkedList mockedList = mock(LinkedList.class);
 //stubbing
 when(mockedList.get(0)).thenReturn("first");
 when(mockedList.get(1)).thenThrow(new RuntimeException());
 //following prints "first"
 System.out.println(mockedList.get(0));
 //following throws runtime exception
 System.out.println(mockedList.get(1));
 //following prints "null" because get(999) was not stubbed
 System.out.println(mockedList.get(999));
 //虽然可以验证存根调用,但通常只是多余的
 //如果我们的代码关心get(0)返回的内容,则其他内容将中断(甚至在执行verify()之前)。
 //如果我们的代码不关心get(0)返回的内容,则不应将其存根。不服气吗?看这里。
 verify(mockedList).get(0);

 

  • 默认情况下,对于所有返回值的方法,Mockito将根据需要返回null,原始/原始包装器值或空集合。例如,对于int / Integer为0,对于boolean / Boolean为false。

  • 存根可以被覆盖:例如,通用存根可以正常设置,但是测试方法可以覆盖它。请注意,过多的存根使代码过于难看和不优雅,这表明存在过多的存根。

  • 一旦存根,该方法将始终返回存根值,而不管其被调用了多少次。

  • 最后一次存根更为重要-当我们多次对具有相同参数的相同方法进行存根时。换句话说:存根的顺序很重要,但很少有意义,例如,存根完全相同的方法调用时或有时使用参数匹配器时,等等。

Mockito - 参数匹配器

Mockito通过使用一种 equals() 方法来验证自然Java风格的参数值。有时,当需要额外的灵活性时,我们可以使用参数匹配器:

//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());
 //argument matchers can also be written as Java 8 Lambdas
 verify(mockedList).add(argThat(someString -> someString.length() > 5));

参数匹配器允许灵活的验证或存根。 查看更多内置匹配器以及自定义参数匹配器参考此文档有关仅自定义参数匹配器的信息,请查看javadoc中的ArgumentMatcher类。使用复杂的参数匹配是合理的。将equals()与 anyX()匹配器配合使用的自然匹配样式倾向于提供简洁的测试。有时最好重构代码以允许equals()匹配,甚至实现equals()方法来帮助进行测试。

关于参数匹配器的注意事项

如果使用参数匹配器,则必须由匹配器提供所有参数。

以下示例显示了验证,但对存根也是如此:

verify(mock).someMethod(anyInt(), anyString(), eq("third argument"));
//这是个正确的例子因为eq()是一个Matcher
verify(mock).someMethod(anyInt(), anyString(), "third argument");
//这是个错误的例子会抛出一个异常因为  "third argument" 不是一个 Matcher 类

诸如anyObject(),eq()之类的匹配器方法不会返回匹配器。在内部,它们在堆栈上记录一个匹配器,并返回一个虚拟值(通常为null)。此实现归因于Java编译器施加的静态类型安全性。结果是我们不能在经过验证/存根的方法之外使用anyObject()和eq()方法。

Mockito - 验证调用次数

 //using mock
 mockedList.add("once");
 mockedList.add("twice");
 mockedList.add("twice");
 mockedList.add("three times");
 mockedList.add("three times");
 mockedList.add("three times");
 //following two verifications work exactly the same - times(1) is used by default
 verify(mockedList).add("once");
 verify(mockedList, times(1)).add("once");
 //exact number of invocations verification
 verify(mockedList, times(2)).add("twice");
 verify(mockedList, times(3)).add("three times");
 //verification using never(). never() is an alias to times(0)
 verify(mockedList, never()).add("never happened");
 //verification using atLeast()/atMost()
 verify(mockedList, atLeastOnce()).add("three times");
 verify(mockedList, atLeast(2)).add("three times");
 verify(mockedList, atMost(5)).add("three times");

默认为times(1)。因此,可以显式地省略使用times(1)。

Mockito - 验证顺序

 // A. Single mock whose methods must be invoked in a particular order
 List singleMock = mock(List.class);
 //using a single mock
 singleMock.add("was added first");
 singleMock.add("was added second");
 //create an inOrder verifier for a single mock
 InOrder inOrder = inOrder(singleMock);
 //following will make sure that add is first called with "was added first, then with "was added second"
 inOrder.verify(singleMock).add("was added first");
 inOrder.verify(singleMock).add("was added second");
 // B. Multiple mocks that must be used in a particular order
 List firstMock = mock(List.class);
 List secondMock = mock(List.class);
 //using mocks
 firstMock.add("was called first");
 secondMock.add("was called second");
 //create inOrder object passing any mocks that need to be verified in order
 InOrder inOrder = inOrder(firstMock, secondMock);
 //following will make sure that firstMock was called before secondMock
 inOrder.verify(firstMock).add("was called first");
 inOrder.verify(secondMock).add("was called second");
 // Oh, and A + B can be mixed together at will

按顺序进行验证是灵活的-我们不必一一验证所有交互,而只需依次验证我们感兴趣的交互即可。同样,我们可以创建一个InOrder对象,该对象仅传递与顺序验证相关的测试。

Mockito - 保模与其他mock未发生相互影响


//using mocks - only mockOne is interacted
 mockOne.a
 dd("one");

 //ordinary verification
 verify(mockOne).add("one");

 //verify that method was never called on a mock
 verify(mockOne, never()).add("two");

 //verify that other mocks were not interacted
 verifyZeroInteractions(mockTwo, mockThree);

Mockito - 找出冗余的调用


//using mocks
 mockedList.add("one");
 mockedList.add("two");

 verify(mockedList).add("one");

 //following verification will fail
 verifyNoMoreInteractions(mockedList);
 

最好使用never()代替

Mockito - 使用@Mock注解

  • 减少重复的模拟创建代码

  • 增加可读性

  • 因为字段名称用于标识Mock,所以使验证错误更易于阅读。

下面我们看一下例子

public class ArticleManagerTest {

   @Mock 
   private ArticleCalculator calculator;
   @Mock 
   private ArticleDatabase database;
   @Mock 
   private UserProvider userProvider;

   private ArticleManager manager;
    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
    }
」

你可以使用MockitoJUnitRunner or a rule: MockitoRule.来运行这个测试类。

Mockito - 使用迭代器方式验证我们的参数

有时,对于同一方法调用,我们需要对不同的返回值/异常进行存根。典型的用例可能是模拟迭代器。Mockito的原始版本没有此功能来促进简单的模拟。例如,可以使用Iterable或简单地使用集合来代替迭代器。这些提供了自然的存根方式(例如使用真实集合)。在极少数情况下,对连续调用进行存根可能会很有用

List a = mock(List.class);
when(a.get(0)).thenReturn("1","2","3");
assertThat(a.get(0),is(equalTo("1")));
assertThat(a.get(0),is(equalTo("2")));
assertThat(a.get(0),is(equalTo("3")));

when(a.get(1)).thenReturn("1").thenReturn("2").thenReturn("3");
assertThat(a.get(1),is(equalTo("1")));
assertThat(a.get(1),is(equalTo("2")));
assertThat(a.get(1),is(equalTo("3")));

注意:如果使用多个具有相同匹配器或参数的存根而不是链接.thenReturn()调用,则每个存根将覆盖前一个存根:

 //All mock.someMethod("some arg") calls will return "two"
 when(mock.someMethod("some arg"))
   .thenReturn("one")
 when(mock.someMethod("some arg"))
   .thenReturn("two")
 

Mockito - spy (间谍)

我们可以创建真实对象的间谍。当我们使用间谍时,将调用实际方法(除非对方法进行了加注)。真正的间谍应该小心谨慎地使用,例如在处理遗留代码时。监视真实对象可以与“部分模拟”概念相关联。在1.8版之前,Mockito间谍并不是真正的部分模拟。

   List list = new LinkedList();
   List spy = spy(list);

   //optionally, you can stub out some methods:
   when(spy.size()).thenReturn(100);

   //using the spy calls *real* methods
   spy.add("one");
   spy.add("two");

   //prints "one" - the first element of a list
   System.out.println(spy.get(0));

   //size() method was stubbed - 100 is printed
   System.out.println(spy.size());

   //optionally, you can verify
   verify(spy).add("one");
   verify(spy).add("two");

有时,使用when(Object)来侦探间谍是不可能或不切实际的。因此,在使用间谍程序时,请考虑使用doReturn | Answer | Throw()系列方法进行存根。例:

   List list = new LinkedList();
   List spy = spy(list);

   //Impossible: real method is called so spy.get(0) throws IndexOutOfBoundsException (the list is yet empty)
   when(spy.get(0)).thenReturn("foo");

   //You have to use doReturn() for stubbing
   doReturn("foo").when(spy).get(0);
 

Mockito *不会*将调用委托给传递的真实实例,而是实际上创建它的副本。因此,如果保留真实实例并与之交互,请不要期望间谍知道这些交互及其对真实实例状态的影响。必然的结果是,当在间谍*上调用*未使用*的方法,而在真实实例上*却未调用*方法时,您将看不到任何对真实实例的影响。注意最终方法。Mockito不模拟最终方法,因此最重要的是:当您监视真实对象时+尝试对最终方法进行存根=麻烦。另外,我们将无法验证这些方法。

Mockito - 重置

聪明的Mockito用户几乎不使用此功能,因为他们知道这可能是测试不佳的迹象。通常,您无需重置模拟,只需为每个测试方法创建新的模拟即可。代替reset(),请考虑在冗长,过度指定的测试中编写简单,小型且集中的测试方法。在测试方法的中间,第一个潜在的代码被reset()。这可能意味着您正在测试太多。请遵循您的测试方法的耳语:“请保持我们的注意力,并专注于单一行为”。

   List mock = mock(List.class);
   when(mock.size()).thenReturn(10);
   mock.add(1);

   reset(mock);
   //at this point the mock forgot any interactions & stubbing

Mockito - BDD 测试

行为驱动开发测试的风格使用//给予//注释,然后将注释作为测试方法的基本组成部分。这正是我们编写测试的方式,我们热烈鼓励这样做!从此处开始了解有关BDD的信息:链接问题在于,当前的stub api具有规范的作用,即word不能与// then / then / then注释很好地集成在一起。这是因为存根属于测试的给定组件,而不属于测试的when组件。因此,BDDMockito类引入了一个别名,以便您可以使用BDDMockito.given(Object)方法对方法调用进行存根。现在,它可以很好地与BDD样式测试的给定组件集成!这是测试的样子:

 import static org.mockito.BDDMockito.*;

 Seller seller = mock(Seller.class);
 Shop shop = new Shop(seller);

 public void shouldBuyBread() throws Exception {
   //given
   given(seller.askForBread()).willReturn(new Bread());

   //when
   Goods goods = shop.buyBread();

   //then
   assertThat(goods, containBread());
 }
 

Mokito -  Verification with timeout

允许验证超时。它使验证等待指定的时间段以进行所需的交互,而不是如果尚未发生则立即失败。在并发条件下进行测试可能有用。我们很少使用这个方法尚未实现与InOrder验证一起使用。

   //passes when someMethod() is called within given time span
   verify(mock, timeout(100)).someMethod();
   //above is an alias to:
   verify(mock, timeout(100).times(1)).someMethod();

   //passes when someMethod() is called *exactly* 2 times within given time span
   verify(mock, timeout(100).times(2)).someMethod();

   //passes when someMethod() is called *at least* 2 times within given time span
   verify(mock, timeout(100).atLeast(2)).someMethod();

   //verifies someMethod() within given time span using given verification mode//useful only if you have your own custom verification modes.
   verify(mock, new Timeout(100, yourOwnVerificationMode)).someMethod();

Mockito - 自定义验证失败消息


// will print a custom message on verification failure
 verify(mock, description("This will print on failure")).someMethod();

 // will work with any verification mode
 verify(mock, times(2).description("someMethod should be called twice")).someMethod();

Mockito - Java 8 Lambda Matcher Support

您可以将Java 8 lambda表达式与ArgumentMatcher一起使用,以减少对ArgumentCaptor的依赖性。如果需要验证对模拟函数调用的输入是否正确,则通常可以使用ArgumentCaptor查找所使用的操作数,然后对它们进行后续声明。虽然对于复杂的示例来说这可能是有用的,但也很费劲。编写lambda很容易。与argThat一起使用时,函数的参数将作为强类型对象传递给ArgumentMatcher,因此可以执行任何操作。


// verify a list only had strings of a certain length added to it// note - this will only compile under Java 8
 verify(list, times(2)).add(argThat(string -> string.length() < 5));

 // Java 7 equivalent - not as neat
 verify(list, times(2)).add(argThat(new ArgumentMatcher(){
     public boolean matches(String arg) {
         return arg.length() < 5;
     }
 }));

 // more complex Java 8 example - where you can specify complex verification behaviour functionally
 verify(target, times(1)).receiveComplexObject(argThat(obj -> obj.getSubObject().get(0).equals("expected")));

 // this can also be used when defining the behaviour of a mock under different inputs// in this case if the input list was fewer than 3 items the mock returns null
 when(mock.someMethod(argThat(list -> list.size()<3))).willReturn(null);

Mockito - when 的使用

启用存根方法。当您希望模拟在调用特定方法时返回特定值时,请使用它。简而言之:“当调用x方法时,返回y”。

Examples:

when(mock.someMethod()).thenReturn(10);

 //you can use flexible argument matchers, e.g:
 when(mock.someMethod(anyString())).thenReturn(10);

 //setting exception to be thrown:
 when(mock.someMethod("some arg")).thenThrow(new RuntimeException());

 //you can set different behavior for consecutive method calls.//Last stubbing (e.g: thenReturn("foo")) determines the behavior of further consecutive calls.
 when(mock.someMethod("some arg"))
  .thenThrow(new RuntimeException())
  .thenReturn("foo");

 //Alternative, shorter version for consecutive stubbing:
 when(mock.someMethod("some arg"))
  .thenReturn("one", "two");
 //is the same as:
 when(mock.someMethod("some arg"))
  .thenReturn("one")
  .thenReturn("two");

 //shorter version for consecutive method calls throwing exceptions:
 when(mock.someMethod("some arg"))
  .thenThrow(new RuntimeException(), new NullPointerException();

Mockito - verify的使用

verify(mock).someMethod("some arg");
//Above is equivalent to:
verify(mock, times(1)).someMethod("some arg");

验证某些行为至少发生一次/确切次数/永不发生。

  verify(mock, times(5)).someMethod("was called five times");

   verify(mock, atLeast(2)).someMethod("was called at least two times");

   //you can use flexible argument matchers, e.g:
   verify(mock, atLeastOnce()).someMethod(anyString());

Mockito - 一些其他的API

doNothing

   doNothing().
   doThrow(new RuntimeException())
   .when(mock).someVoidMethod();

   //does nothing the first time:
   mock.someVoidMethod();

   //throws RuntimeException the next time:
   mock.someVoidMethod();
 
 
 
   List list = new LinkedList();
   List spy = spy(list);

   //let's make clear() do nothing
   doNothing().when(spy).clear();

   spy.add("one");

   //clear() does nothing, so the list still contains "one"
   spy.clear();
 

 

使用doNothing()将void方法设置为不执行任何操作。注意,默认情况下,模拟上的void方法什么也不做!但是,在少数情况下,doNothing()会派上用场

doReturn

在无法使用when(Object)的极少数情况下,请使用doReturn()。请注意,始终建议对when(Object)进行存根,因为它是参数类型安全的,并且更具可读性(尤其是在存根连续调用时)。以下是doReturn()派上用场的那些罕见情况:

监视真实对象并在间谍上调用真实方法时会带来副作用

   List list = new LinkedList();
   List spy = spy(list);

   //Impossible: real method is called so spy.get(0) throws IndexOutOfBoundsException (the list is yet empty)
   when(spy.get(0)).thenReturn("foo");

   //You have to use doReturn() for stubbing:
   doReturn("foo").when(spy).get(0);
   
   
   //Overriding a previous exception-stubbing
   when(mock.foo()).thenThrow(new RuntimeException());
  
    //Impossible: the exception-stubbed foo() method is called so RuntimeException is thrown.
   when(mock.foo()).thenReturn("bar");

   //You have to use doReturn() for stubbing:
   doReturn("bar").when(mock).foo();
   
   List list = new LinkedList();
   List spy = spy(list);

   //Impossible: real method is called so spy.get(0) throws IndexOutOfBoundsException (the list is yet empty)
   when(spy.get(0)).thenReturn("foo", "bar", "qix");

   //You have to use doReturn() for stubbing:
   doReturn("foo", "bar", "qix").when(spy).get(0);
     
      
   when(mock.foo()).thenThrow(new RuntimeException());

   //Impossible: the exception-stubbed foo() method is called so RuntimeException is thrown.
   when(mock.foo()).thenReturn("bar", "foo", "qix");

   //You have to use doReturn() for stubbing:
   doReturn("bar", "foo", "qix").when(mock).foo();
 

以上方案显示了Mockito优雅语法的折中方案。请注意,这种情况很少见。间谍活动应该是零星的,并且很少使用异常处理。更不用说总的来说,过度存根是一种潜在的代码异味,它指出了太多的存根。

inOrder

   InOrder inOrder = inOrder(firstMock, secondMock);

   inOrder.verify(firstMock).add("was called first");
   inOrder.verify(secondMock).add("was called second");

ignoreStubs

//mocking lists for the sake of the example (if you mock List in real you will burn in hell)
  List mock1 = mock(List.class), mock2 = mock(List.class);

  //stubbing mocks:
  when(mock1.get(0)).thenReturn(10);
  when(mock2.get(0)).thenReturn(20);

  //using mocks by calling stubbed get(0) methods:
  System.out.println(mock1.get(0)); //prints 10
  System.out.println(mock2.get(0)); //prints 20//using mocks by calling clear() methods:
  mock1.clear();
  mock2.clear();

  //verification:
  verify(mock1).clear();
  verify(mock2).clear();

  //verifyNoMoreInteractions() fails because get() methods were not accounted for.try { verifyNoMoreInteractions(mock1, mock2); } catch (NoInteractionsWanted e);

  //However, if we ignore stubbed methods then we can verifyNoMoreInteractions()
  verifyNoMoreInteractions(ignoreStubs(mock1, mock2));

  //Remember that ignoreStubs() *changes* the input mocks and returns them for convenience.
  
  List list = mock(List.class);
  when(mock.get(0)).thenReturn("foo");

  list.add(0);
  System.out.println(list.get(0)); //we don't want to verify this
  list.clear();

  InOrder inOrder = inOrder(ignoreStubs(list));
  inOrder.verify(list).add(0);
  inOrder.verify(list).clear();
  inOrder.verifyNoMoreInteractions();

忽略存根可以按顺序用于验证

times

Allows verifying exact number of invocations. E.g:

verify(mock, times(2)).someMethod("some arg");

never

Verifies that interaction did not happen. E.g:


   verify(mock, never()).someMethod();
 

atLeastOnce

verification modeatLeastOnce

verification. E.g:

verify(mock, atLeastOnce()).someMethod("some arg");

atLeast

verification. E.g:

verify(mock, atLeast(3)).someMethod("some arg");

atMost

E.g:

  verify(mock, atMost(3)).someMethod("some arg");

calls

For example


   inOrder.verify( mock, calls( 2 )).someMethod( "some arg" );
 

only

E.g:


   verify(mock, only()).someMethod();
   //above is a shorthand for following 2 lines of code:
   verify(mock).someMethod();
   verifyNoMoreInvocations(mock);
 

timeout


   //passes when someMethod() is called within given time span
   verify(mock, timeout(100)).someMethod();
   //above is an alias to:
   verify(mock, timeout(100).times(1)).someMethod();

   //passes as soon as someMethod() has been called 2 times before the given timeout
   verify(mock, timeout(100).times(2)).someMethod();

   //equivalent: this also passes as soon as someMethod() has been called 2 times before the given timeout
   verify(mock, timeout(100).atLeast(2)).someMethod();

   //verifies someMethod() within given time span using given verification mode
   //useful only if you have your own custom verification modes.
   verify(mock, new Timeout(100, yourOwnVerificationMode)).someMethod();
 

after


   //passes after 100ms, if someMethod() has only been called once at that time.
   verify(mock, after(100)).someMethod();
   //above is an alias to:
   verify(mock, after(100).times(1)).someMethod();

   //passes if someMethod() is called *exactly* 2 times after the given timespan
   verify(mock, after(100).times(2)).someMethod();

   //passes if someMethod() has not been called after the given timespan
   verify(mock, after(100).never()).someMethod();

   //verifies someMethod() after a given time span using given verification mode
   //useful only if you have your own custom verification modes.
   verify(mock, new After(100, yourOwnVerificationMode)).someMethod();
 

 

你可能感兴趣的:(Android,单元测试(Unit,test))