0%

安卓测试-Mockito框架

Mockito是一个用于java单元测试中的mocking框架,mock就是模拟的意思,就是能够模拟一些类和方法的实现。当需要测试的类需要一个依赖类的时候,如果依赖类的获取很麻烦,那么可以直接使用Mock方法来模拟这个类,方便测试。

官方文档

引入

build.gradle里面增加

1
2
testImplementation "org.mockito:mockito-core:+"
androidTestImplementation "org.mockito:mockito-android:+" //看了下好像没几个类,可能以后会增加功能

常用方法

验证行为

1
2
3
4
5
6
7
8
9
10
11
12
13
14

//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
verify(mockedList).add("one");
verify(mockedList).clear();

Stubbing

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 //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());

doThrow(new RuntimeException()).when(mockedList).clear();

//following throws RuntimeException:
mockedList.clear();

//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));

//Although it is possible to verify a stubbed invocation, usually it's just redundant
//If your code cares what get(0) returns, then something else breaks (often even before verify() gets executed).
//If your code doesn't care what get(0) returns, then it should not be stubbed. Not convinced? See here.
verify(mockedList).get(0);

参数匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//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));

验证方法的调用次数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

//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");

验证方法的调用顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 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

验证方法没有执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//using mocks - only mockOne is interacted
mockOne.add("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);



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

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

//following verification will fail
verifyNoMoreInteractions(mockedList);

doReturn()|doThrow()| doAnswer()|doNothing()|doCallRealMethod()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

doThrow(new RuntimeException()).when(mockedList).clear();

//following throws RuntimeException:
mockedList.clear();


when(mock.someMethod(anyString())).thenAnswer(
new Answer() {
public Object answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
Object mock = invocation.getMock();
return "called with arguments: " + Arrays.toString(args);
}
});

//Following prints "called with arguments: [foo]"
System.out.println(mock.someMethod("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");

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

参数捕捉

1
2
3
4
5
//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);

序列化的mock

1
List serializableMock = mock(List.class, withSettings().serializable());

验证超时

1
2
3
4
5
6
7
8
9
10
11
12

//passes when someMethod() is called no later than within 100 ms
//exits immediately when verification is satisfied (e.g. may not wait full 100 ms)
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 under 100 ms
verify(mock, timeout(100).times(2)).someMethod();

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

mock抽象类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//convenience API, new overloaded spy() method:
SomeAbstract spy = spy(SomeAbstract.class);

//Mocking abstract methods, spying default methods of an interface (only available since 2.7.13)
Function function = spy(Function.class);

//Robust API, via settings builder:
OtherAbstract spy = mock(OtherAbstract.class, withSettings()
.useConstructor().defaultAnswer(CALLS_REAL_METHODS));

//Mocking an abstract class with constructor arguments (only available since 2.7.14)
SomeAbstract spy = mock(SomeAbstract.class, withSettings()
.useConstructor("arg1", 123).defaultAnswer(CALLS_REAL_METHODS));

//Mocking a non-static inner abstract class:
InnerAbstract spy = mock(InnerAbstract.class, withSettings()
.useConstructor().outerInstance(outerInstance).defaultAnswer(CALLS_REAL_METHODS));

自定义验证失败信息

1
2
3
4
5
6
// 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();

Java8匹配器支持

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

// 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))).thenReturn(null);

Java8 Answer接口支持

1
2
3
4
5
6
7
8
9
// answer by returning 12 every time
doAnswer(invocation -> 12).when(mock).doSomething();

// answer by using one of the parameters - converting into the right
// type as your go - in this case, returning the length of the second string parameter
// as the answer. This gets long-winded quickly, with casting of parameters.
doAnswer(invocation -> ((String)invocation.getArgument(1)).length())
.when(mock).doSomething(anyString(), anyString(), anyString());

对final对象,方法,枚举的支持

1
2
3
4
5
6
7
8
9
10
final class FinalClass {
final String finalMethod() { return "something"; }
}

FinalClass concrete = new FinalClass();

FinalClass mock = mock(FinalClass.class);
given(mock.finalMethod()).willReturn("not anymore");

assertThat(mock.finalMethod()).isNotEqualTo(concrete.finalMethod());

创建mock对象的方法

注解方法创建

1
2
3
4
5
6
7
8
9

public class ArticleManagerTest {

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

private ArticleManager manager;

使用注解方法创建的时候,需要先

1
MockitoAnnotations.initMocks(this);

也可以使用预置的 MockitoJUnitRunner Runner 或者 rule: MockitoRule.

直接创建

1
Person mPerson = mock(Person.class); //<--使用mock方法

使用spy监视真正的对象

你可以创建真实对象的spy对象,当使用spy对象的时候真实的方法会被调用(假如这个方法没有被stub)
应当谨慎小心的使用spy对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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());

对spy对象要慎用when/thenReturn

1
2
3
4
5
6
7
8
9
10
//optionally, you can verify
verify(spy).add("one");
verify(spy).add("two");

//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);