1.官方文档
mockit官网
https://site.mockito.org
mockit api
https://javadoc.io/doc/org.mockito/mockito-core/latest/index.html
mockit源码
https://github.com/mockito/mockito
mockit教程
https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html
kotlin扩展
https://github.com/mockito/mockito-kotlin
2.模拟接口、抽象类、内部抽象类
2.1 模拟接口
1 interface Interface1 { fun say() = "said" }
2 interface Interface2 : Interface1 { override fun say() = "said" }
4 @RunWith(AndroidJUnit4::class)
5 class interface_test(){
6 @Test @SmallTest //测试 interface
7 fun test_mock_interface(){
8 // mock creation
9 val mock = Mockito.mock(Interface1::class.java)
10 // when
11 Mockito.`when`(mock.say()).thenReturn("mock said")
12 // then
13 assertTrue(mock.say() == "mock said")
15 // spy
16 val spy = spy(Interface1::class.java)
17 Mockito.`when`(spy.say()).thenReturn("spy said")
18 assertTrue(spy.say() == "spy said")
19 }
2.2 模拟抽象类
使用mock()并指定setting可以生成 任意抽象类 的模拟对象
使用spy()无法对 构造有参数的抽象类 生成 模拟对象。
1 abstract class Abstract1 { fun say() = "said" }
2 abstract class Abstract2(var name: String) { fun say() = "said" }
4 @RunWith(AndroidJUnit4::class)
5 class Abstract_test(){
7 @Test @SmallTest //测试无构造参数的 abstract 类
8 fun test_mock_abstract(){
9 // mock creation
10 val mock = Mockito.mock(Abstract1::class.java)
11 // when
12 Mockito.`when`(mock.say()).thenReturn("mock said")
13 var words = mock.say()
14 // then
15 Mockito.verify(mock).say()
16 assertTrue(words == "mock said")
18 // spy
19 val setting = Mockito.withSettings().useConstructor().defaultAnswer(CALLS_REAL_METHODS)
20 val spy = spy(Abstract1::class.java)
21 Mockito.`when`(spy.say()).thenReturn("spy said")
22 words = spy.say()
23 Mockito.verify(spy).say()
24 assertTrue(words == "spy said")
25 }
26 @Test @SmallTest //测试有构造参数的 abstract 类,这时spy无法通过
27 fun test_mock_abstract_args(){
28 // spy
29 // val spy = spy(Abstract2::class.java) // error,无法spy
30 val setting = Mockito.withSettings().useConstructor("args").defaultAnswer(CALLS_REAL_METHODS)
31 val spy = Mockito.mock(Abstract2::class.java,setting)
32 Mockito.`when`(spy.say()).thenReturn("spy said")
33 val words = spy.say()
34 Mockito.verify(spy).say()
35 assertTrue(words == "spy said")
36 }
37 @Test @SmallTest //测试有构造参数的 inner abstract 类
38 fun test_mock_abstract_inner(){
39 abstract class Abstract3(var name: String) { fun say() = "said" }
40 // spy
41 val setting = Mockito.withSettings().useConstructor("inner")/*.outerInstance(outerInstance)*/.defaultAnswer(CALLS_REAL_METHODS)
42 val spy = Mockito.mock(Abstract3::class.java,setting)
43 Mockito.`when`(spy.say()).thenReturn("inner said")
44 val words = spy.say()
45 Mockito.verify(spy).say()
46 assertTrue(words == "inner said")
47 }
3.打桩静态方法
3.1 要求
使用 inline mock maker
java要使用 try-with-resources 语法,kotlin可使用use()
3.2 示例
java 代码 :
1 @RunWith(AndroidJUnit4.class)
2 public class MockitoJava {
3 public static int value() { return -1;}
5 @Test @SmallTest
6 public void testMockStatic(){
7 assertTrue(-1 == MockitoJava.value());
8 try(MockedStatic mock = Mockito.mockStatic(MockitoJava.class)) {
9 mock.when(MockitoJava::value).thenReturn(100);
10 assertTrue(100 == MockitoJava.value());
11 //超出范围后结束
12 }
13 assertTrue(-1 == MockitoJava.value());
14 }
kotlin 暂时不支持
1 @Test @SmallTest
2 fun test_mock_static2(){
3 // given
4 Mockito.mockStatic(StuStatic1::class.java).use {
5 // when
6 Mockito.`when`(StuStatic1.value1()).thenReturn(200 )
7 // then
8 assertTrue(StuStatic1.value1() == 200 )
10 it
11 }
4.临时打桩构造函数
用Mockito.mockConstruction()临时打桩构造函数,在它的作用域内,new出来的对象都是模拟对象。
4.1 要求
使用 inline mock maker
java要使用 try-with-resources 语句,kotlin可使用use()
4.2 java示例
1 @RunWith(AndroidJUnit4.class)
2 public class MockitoJava {
3 @Test @SmallTest
4 public void testConstruction(){
5 class Student { public int run() { return 1;} }
7 assertTrue(new Student().run() == 1 );
8 try(MockedConstruction mocked = Mockito.mockConstruction(Student.class)){
9 Student stu = new Student();
10 assertTrue(stu.run() == 0 ); //0,not 1
11 Mockito.when(stu.run()).thenReturn(10);
12 assertTrue(stu.run() == 10 );
13 }
14 assertTrue(new Student().run() == 1 ); //ok
15 }
第8-13行是个临时作用域,使用了java的try-with-resources 语法
使用Mockito.mockConstruction生成了一个MockedConstruction对象。
在第8-13行内,new 的student对象都是模拟对象,且可打桩(第11行)。
第10行模拟对象默认是返回0值的。
4.3 kotlin示例
1 @Test @SmallTest
2 fun test_mocking_construction(){
3 // given
4 open class Student{ fun run() = 1 fun stop() = 2}
6 Mockito.mockConstruction(Student::class.java).use {
7 val student = Student()
8 assertTrue(student.run() == 0 ) //ok
9 `when`(student.run()).thenReturn(10)
10 assertTrue(student.run() == 10)
11 }
12 assertTrue(Student().run () == 10) //failed
结果同4.2
5.kotlin 拓展库
源码: https://github.com/mockito/mockito-kotlin
示例: https://github.com/mockito/mockito-kotlin/tree/main/tests/src/test/kotlin/test
5.1 引入扩展库
testImplementation "org.mockito.kotlin:mockito-kotlin:4.0.0"
扩展库最新版本可在github上找,不是在mockito官网上.它可能比mockito库更新的慢。
5.3 简单示例
1 @RunWith(AndroidJUnit4::class)
2 class MockitoKotlin {
3 @Test @SmallTest
4 fun mockito_kotlin_test_mock(){
5 open class Student{ fun value() = 1}
6 val mock = mock<Student>{
7 on { value() } doReturn(10)
9 assertTrue(mock.value() == 10)
11 val spy = spy<Student>{
12 on(it.value()).thenReturn(10)
13 }
14 assertTrue(spy.value() == 10)
15 Mockito.reset(spy)
16 assertTrue(spy.value() == 1 )
17 }
18 @Test @SmallTest
19 fun mockito_kotlin_test_doNothing(){
20 open class Student(var v : Int = -1) {
21 fun value(){
22 println("kotlin doNothing")
23 }
24 }
25 val mock = mock<Student> {
26 doNothing().`when`(it).value()
27 }
28 mock.value()
29 verify(mock).value()
30 }
使用 on .. thenReturn 对应 when...thenReturn
5.4 更多示例
5.4.1 mock
1 @Test
2 fun propertyClassVariable() {
3 /* When */
4 propertyClassVariable = mock()
6 /* Then */
7 expect(propertyClassVariable).toNotBeNull()
10 @Test
11 fun untypedVariable() {
12 /* When */
13 val instance = mock<MyClass>()
15 expect(instance).toNotBeNull()
16 }
18 @Test
19 fun deepStubs() {
20 val cal: Calendar = mock(defaultAnswer = Mockito.RETURNS_DEEP_STUBS)
21 whenever(cal.time.time).thenReturn(123L)
22 expect(cal.time.time).toBe(123L)
23 }
26 @Test
27 fun testMockStubbing_lambda() {
28 /* Given */
29 val mock = mock<Open>() {
30 on { stringResult() } doReturn "A"
31 }
33 /* When */
34 val result = mock.stringResult()
36 /* Then */
37 expect(result).toBe("A")
5.4.2 spy
1 @Test
2 fun spyInterfaceInstance() {
3 /* When */
4 val result = spy(interfaceInstance)
6 /* Then */
7 expect(result).toNotBeNull()
10 @Test
11 fun spyOpenClassInstance() {
12 /* When */
13 val result = spy(openClassInstance)
15 /* Then */
16 expect(result).toNotBeNull()
17 }
19 @Test
20 fun doReturnWithSpy() {
21 val date = spy(Date())
22 doReturn(123L).whenever(date).time
23 expect(date.time).toBe(123L)
24 }
26 @Test
27 fun doNothingWithSpy() {
28 val date = spy(Date(0))
29 doNothing().whenever(date).time = 5L
30 date.time = 5L
31 expect(date.time).toBe(0L)
32 }
34 @Test(expected = IllegalArgumentException::class)
35 fun doThrowWithSpy() {
36 val date = spy(Date(0))
37 doThrow(IllegalArgumentException()).whenever(date).time
38 date.time
39 }
41 @Test
42 fun doCallRealMethodWithSpy() {
43 val date = spy(Date(0))
44 doReturn(123L).whenever(date).time
45 doCallRealMethod().whenever(date).time
46 expect(date.time).toBe(0L)
47 }
49 @Test
50 fun doReturnWithDefaultInstanceSpyStubbing() {
51 val timeVal = 12L
53 val dateSpy = spy<Date> {
54 on { time } doReturn timeVal
55 }
57 expect(dateSpy.time).toBe(timeVal)
58 }
60 @Test
61 fun doReturnWithSpyStubbing() {
62 val timeVal = 15L
64 val dateSpy = spy(Date(0)) {
65 on { time } doReturn timeVal
66 }
68 expect(dateSpy.time).toBe(timeVal)
69 }
71 @Test
72 fun passAnyStringToSpy() {
73 /* Given */
74 val my = spy(MyClass())
76 /* When */
77 doReturn("mocked").whenever(my).foo(any())
79 /* Then */
80 expect(my.foo("hello")).toBe("mocked")
5.4.3 Matchers
1 @Test
2 fun anyString() {
3 mock<Methods>().apply {
4 string("")
5 verify(this).string(any())
9 @Test
10 fun anyInt() {
11 mock<Methods>().apply {
12 int(3)
13 verify(this).int(any())
14 }
15 }
17 @Test
18 fun anyClosedClass() {
19 mock<Methods>().apply {
20 closed(Closed())
21 verify(this).closed(any())
22 }
23 }
25 @Test
26 fun anyIntArray() {
27 mock<Methods>().apply {
28 intArray(intArrayOf())
29 verify(this).intArray(any())
30 }
31 }
33 @Test
34 fun anyClassArray() {
35 mock<Methods>().apply {
36 closedArray(arrayOf(Closed()))
37 verify(this).closedArray(anyArray())
38 }
39 }
41 @Test
42 fun anyNullableClassArray() {
43 mock<Methods>().apply {
44 closedNullableArray(arrayOf(Closed(), null))
45 verify(this).closedNullableArray(anyArray())
46 }
47 }
49 @Test
50 fun anyStringVararg() {
51 mock<Methods>().apply {
52 closedVararg(Closed(), Closed())
53 verify(this).closedVararg(anyVararg())
54 }
55 }
57 @Test
58 fun anyNull_neverVerifiesAny() {
59 mock<Methods>().apply {
60 nullableString(null)
61 verify(this, never()).nullableString(any())
62 }
63 }
65 @Test
66 fun anyNull_verifiesAnyOrNull() {
67 mock<Methods>().apply {
68 nullableString(null)
69 verify(this).nullableString(anyOrNull())
70 }
71 }
73 @Test
74 fun anyNull_forPrimitiveBoolean() {
75 mock<Methods>().apply {
76 boolean(false)
77 verify(this).boolean(anyOrNull())
78 }
79 }
80 @Test
81 fun anyNull_forPrimitiveByte() {
82 mock<Methods>().apply {
83 byte(3)
84 verify(this).byte(anyOrNull())
85 }
5.4.4 eq()
1 @Test
2 fun eqOpenClassInstance() {
3 /* When */
4 val result = eq(openClassInstance)
6 /* Then */
7 expect(result).toNotBeNull()
10 @Test
11 fun eqClosedClassInstance() {
12 /* When */
13 val result = eq(closedClassInstance)
15 /* Then */
16 expect(result).toNotBeNull()
17 }
19 @Test
20 fun nullArgument() {
21 /* Given */
22 val s: String? = null
24 /* When */
25 val result = eq(s)
27 /* Then */
28 expect(result).toBeNull()
5.4.5 verfity()
1 @Test
2 fun verify0Calls() {
3 val iface = mock<TestInterface>()
5 verify(iface) {
6 0 * { call(any()) }
10 @Test
11 fun verifyNCalls() {
12 val iface = mock<TestInterface>()
14 iface.call(42)
15 iface.call(42)
17 verify(iface) {
18 2 * { call(42) }
19 }
20 }
22 @Test(expected = TooFewActualInvocations::class)
23 fun verifyFailsWithWrongCount() {
24 val iface = mock<TestInterface>()
26 iface.call(0)
28 verify(iface) {
29 2 * { call(0) }
30 }
31 }
33 @Test(expected = ArgumentsAreDifferent::class)
34 fun verifyFailsWithWrongArg() {
35 val iface = mock<TestInterface>()
37 iface.call(3)
39 verify(iface) {
40 1 * { call(0) }
41 }
42 }
44 @Test
45 fun verifyDefaultArgs_firstParameter() {
46 /* Given */
47 val m = mock<TestInterface>()
49 /* When */
50 m.defaultArgs(a = 2)
52 /* Then */
53 verify(m).defaultArgs(2)
54 }
56 @Test
57 fun verifyDefaultArgs_secondParameter() {
58 /* Given */
59 val m = mock<TestInterface>()
61 /* When */
62 m.defaultArgs(b = 2)
64 /* Then */
65 verify(m).defaultArgs(b = 2)
5.4.6 ArgumentCaptor
1 @Test
2 fun argumentCaptor_destructuring2() {
3 /* Given */
4 val date: Date = mock()
6 /* When */
7 date.time = 5L
9 /* Then */
10 val (captor1, captor2) = argumentCaptor<Long, Long>()
11 verify(date).time = captor1.capture()
12 verify(date).time = captor2.capture()
13 expect(captor1.lastValue).toBe(5L)
14 expect(captor2.lastValue).toBe(5L)
15 }
17 @Test
18 fun argumentCaptor_destructuring3() {
19 /* Given */
20 val date: Date = mock()
22 /* When */
23 date.time = 5L
25 /* Then */
26 val (captor1, captor2, captor3) = argumentCaptor<Long, Long, Long>()
27 val verifyCaptor: KArgumentCaptor<Long>.() -> Unit = {
28 verify(date).time = capture()
29 expect(lastValue).toBe(5L)
30 }
31 captor1.apply(verifyCaptor)
32 captor2.apply(verifyCaptor)
33 captor3.apply(verifyCaptor)
34 }
36 @Test
37 fun argumentCaptor_destructuring4() {
38 /* Given */
39 val date: Date = mock()
41 /* When */
42 date.time = 5L
44 /* Then */
45 val (captor1, captor2, captor3, captor4) = argumentCaptor<Long, Long, Long, Long>()
46 val verifyCaptor: KArgumentCaptor<Long>.() -> Unit = {
47 verify(date).time = capture()
48 expect(lastValue).toBe(5L)
49 }
50 captor1.apply(verifyCaptor)
51 captor2.apply(verifyCaptor)
52 captor3.apply(verifyCaptor)
53 captor4.apply(verifyCaptor)
5.4.7 OngoingStubbing
1 @Test
2 fun testOngoingStubbing_methodCall() {
3 /* Given */
4 val mock = mock<Open>()
5 mock<Open> {
6 on(mock.stringResult()).doReturn("A")
9 /* When */
10 val result = mock.stringResult()
12 /* Then */
13 expect(result).toBe("A")
14 }
16 @Test
17 fun testOngoingStubbing_builder() {
18 /* Given */
19 val mock = mock<Methods> { mock ->
20 on { builderMethod() } doReturn mock
21 }
23 /* When */
24 val result = mock.builderMethod()
26 /* Then */
27 expect(result).toBeTheSameAs(mock)
28 }
30 @Test
31 fun testOngoingStubbing_nullable() {
32 /* Given */
33 val mock = mock<Methods> {
34 on { nullableStringResult() } doReturn "Test"
35 }
37 /* When */
38 val result = mock.nullableStringResult()
40 /* Then */
41 expect(result).toBe("Test")
42 }
44 @Test
45 fun testOngoingStubbing_doThrow() {
46 /* Given */
47 val mock = mock<Methods> {
48 on { builderMethod() } doThrow IllegalArgumentException()
49 }
51 try {
52 /* When */
53 mock.builderMethod()
54 fail("No exception thrown")
55 } catch (e: IllegalArgumentException) {
56 }
57 }
59 @Test
60 fun testOngoingStubbing_doThrowClass() {
61 /* Given */
62 val mock = mock<Methods> {
63 on { builderMethod() } doThrow IllegalArgumentException::class
64 }
66 try {
67 /* When */
68 mock.builderMethod()
69 fail("No exception thrown")
70 } catch (e: IllegalArgumentException) {
71 }
72 }
74 @Test
75 fun testOngoingStubbing_doThrowVarargs() {
76 /* Given */
77 val mock = mock<Methods> {
78 on { builderMethod() }.doThrow(
79 IllegalArgumentException(),
80 UnsupportedOperationException()
81 )
82 }
84 try {
85 /* When */
86 mock.builderMethod()
87 fail("No exception thrown")
88 } catch (e: IllegalArgumentException) {
89 }
91 try {
92 /* When */
93 mock.builderMethod()
94 fail("No exception thrown")
95 } catch (e: UnsupportedOperationException) {
96 }
5.4.8 完整
详见:https://github.com/mockito/mockito-kotlin/tree/main/tests/src/test/kotlin/test
5.5 支持打桩扩展函数吗?
1 // 测试扩展
2 @Test @SmallTest
3 fun test_mock_kotlin_ext(){
4 // given
5 open class Student{ fun run() = 1 }
6 fun Student.stop() = 2
8 val mock = Mockito.spy(Student::class.java)
9 assertTrue(mock.stop() == 2)
11 // when
12 Mockito.`when`(mock.run()).thenReturn(10)
13 Mockito.`when`(mock.stop()).thenReturn(20)
15 // then
16 assertTrue(mock.stop() == 20)
报错如下:
when() requires an argument which has to be 'a method call on a mock'.
For example:
when(mock.getArticles()).thenReturn(articles);
Also, this error might show up because:
1. you stub either of: final/private/equals()/hashCode() methods.
Those methods *cannot* be stubbed/verified.
Mocking methods declared on non-public parent classes is not supported.
2. inside when() you don't call method on mock but on some other object.
6.打桩模板
6.1 系统常用类模板
1 @Test @SmallTest
2 fun test_mock_template1(){
3 // given
4 val list /*: MutableList<Any>*/ = Mockito.mock(MutableList::class.java)
5 val hashMap = Mockito.mock(HashMap::class.java)
6 val hashSet = Mockito.mock(HashSet::class.java)
8 // list.add("item0")
9 // when
10 Mockito.`when`(list[0]).thenReturn("item0")
11 Mockito.`when`(hashMap["key"]).thenReturn("value")
12 Mockito.`when`(hashSet.size).thenReturn(20)
14 // then
15 assertTrue(list[0] == "item0")
16 assertTrue(hashMap["key"] == "value")
17 assertTrue(hashSet.size == 20 )
6.2 自定义的类模板并针对特定类型打桩
如对 UUU<String> 的实例打桩。
示例1(java)
1 @Test @SmallTest
2 public void test_template(){
3 class UUU <T> { public int values(T ... ts){ return ts.length; } }
5 UUU<String> mock = Mockito.mock(UUU.class);
7 Mockito.when(mock.values(Mockito.any())).thenReturn(20);
8 assertTrue(mock.values("v1","v2","v3") == 20);
这里生成了一个UUU<String>类型的模拟对象,可对它打桩。
示例2(Kotlin)
1 @Test @SmallTest
2 fun test_mock_template3() {
3 open class UUU <T> { fun values(vararg args : T?) = args.size }
4 //val mock1 = Mockito.mock(UUU<String>::class.java) //failed
5 val mock2 = Mockito.mock(UUU::class.java) //ok,但是没有指定类型
6 val mock3 = Mockito.mock(UUU<String>()::class.java) //ok,正解。
7 val spy1 = Mockito.spy(UUU<String>()) //ok
9 Mockito.`when`(mock3.values(any())).thenReturn(20)
10 Mockito.`when`(spy1.values(any())).thenReturn (30)
11 assertTrue(mock3.values("v1","v2","v5") == 20)
12 assertTrue(spy1.values ("v1","v2","v5") == 30)
第4行编译不过,Kotlin 和 java 都不支持 UUU<String>::class.java 这种写法。
第5行虽然编译通过,它生成的是UUU<*>类型,不是具体类型,无意义。
第6行的红色()是关键,先生成个对象就可得到Class< UUU<String>> 了。
第7行 spy函数使用不受UUU<String>::class.java 这种语法影响。
6.3 mockito-kotlin 拓展库
虽然 mockito-inline库中,有 UUU<String>::class.java 这种语法影响,但是mockito-kotlin库中没有。
1 @Test @SmallTest
2 fun mockito_kotlin_test_template(){
3 val list = mock<List<String>> {
4 `when`(it[0]).thenReturn("one")
6 val map = mock<Map<String,Int>> {
7 `when`(it["key1"]).thenReturn(1)
9 val set = mock<MutableSet<String>> {
10 //doNothing().`when`(it).clear()
11 `when`(it.size).thenReturn(1)
12 }
13 assertTrue(list[0] == "one")
14 assertTrue(map["key1"] == 1 )
15 assertTrue(set.size == 1 )
17 set.clear()
18 verify(set).clear()
6.4 打桩成员函数模板
1 @Test @SmallTest
2 fun test_mock_template4() {
3 open class UUU{ inline fun < reified T : Any> values(vararg args : T?) = args.size }
4 val mock = Mockito.mock(UUU::class.java)
5 `when`(mock.values<String>(Mockito.any())).thenReturn(10)
6 assertTrue(mock.values("v1","v2","v5") == 10)
8 `when`(mock.values<Int>(Mockito.any())).thenReturn(20)
9 assertTrue(mock.values(1,2,3) == 20)