相关文章推荐
没有腹肌的麦片  ·  Azure Firewall rule ...·  1 年前    · 

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)