1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.util.mockito 18 19 /** 20 * Kotlin versions of popular mockito methods that can return null in situations when Kotlin expects 21 * a non-null value. Kotlin will throw an IllegalStateException when this takes place ("x must not 22 * be null"). To fix this, we can use methods that modify the return type to be nullable. This 23 * causes Kotlin to skip the null checks. 24 */ 25 26 import org.mockito.ArgumentCaptor 27 import org.mockito.ArgumentMatcher 28 import org.mockito.Mockito 29 import org.mockito.Mockito.`when` 30 import org.mockito.stubbing.OngoingStubbing 31 import org.mockito.stubbing.Stubber 32 33 /** 34 * Returns Mockito.eq() as nullable type to avoid java.lang.IllegalStateException when 35 * null is returned. 36 * 37 * Generic T is nullable because implicitly bounded by Any?. 38 */ 39 fun <T> eq(obj: T): T = Mockito.eq<T>(obj) 40 41 /** 42 * Returns Mockito.any() as nullable type to avoid java.lang.IllegalStateException when 43 * null is returned. 44 * 45 * Generic T is nullable because implicitly bounded by Any?. 46 */ 47 fun <T> any(type: Class<T>): T = Mockito.any<T>(type) 48 inline fun <reified T> any(): T = any(T::class.java) 49 50 /** 51 * Returns Mockito.argThat() as nullable type to avoid java.lang.IllegalStateException when 52 * null is returned. 53 * 54 * Generic T is nullable because implicitly bounded by Any?. 55 */ 56 fun <T> argThat(matcher: ArgumentMatcher<T>): T = Mockito.argThat(matcher) 57 58 /** 59 * Kotlin type-inferred version of Mockito.nullable() 60 */ 61 inline fun <reified T> nullable(): T? = Mockito.nullable(T::class.java) 62 63 /** 64 * Returns ArgumentCaptor.capture() as nullable type to avoid java.lang.IllegalStateException 65 * when null is returned. 66 * 67 * Generic T is nullable because implicitly bounded by Any?. 68 */ 69 fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture() 70 71 /** 72 * Helper function for creating an argumentCaptor in kotlin. 73 * 74 * Generic T is nullable because implicitly bounded by Any?. 75 */ 76 inline fun <reified T : Any> argumentCaptor(): ArgumentCaptor<T> = 77 ArgumentCaptor.forClass(T::class.java) 78 79 /** 80 * Helper function for creating new mocks, without the need to pass in a [Class] instance. 81 * 82 * Generic T is nullable because implicitly bounded by Any?. 83 * 84 * @param apply builder function to simplify stub configuration by improving type inference. 85 */ 86 inline fun <reified T : Any> mock(apply: T.() -> Unit = {}): T = Mockito.mock(T::class.java) 87 .apply(apply) 88 89 /** 90 * Helper function for stubbing methods without the need to use backticks. 91 * 92 * @see Mockito.when 93 */ 94 fun <T> whenever(methodCall: T): OngoingStubbing<T> = `when`(methodCall) 95 fun <T> Stubber.whenever(mock: T): T = `when`(mock) 96 97 /** 98 * A kotlin implemented wrapper of [ArgumentCaptor] which prevents the following exception when 99 * kotlin tests are mocking kotlin objects and the methods take non-null parameters: 100 * 101 * java.lang.NullPointerException: capture() must not be null 102 */ 103 class KotlinArgumentCaptor<T> constructor(clazz: Class<T>) { 104 private val wrapped: ArgumentCaptor<T> = ArgumentCaptor.forClass(clazz) 105 fun capture(): T = wrapped.capture() 106 val value: T 107 get() = wrapped.value 108 val allValues: List<T> 109 get() = wrapped.allValues 110 } 111 112 /** 113 * Helper function for creating an argumentCaptor in kotlin. 114 * 115 * Generic T is nullable because implicitly bounded by Any?. 116 */ 117 inline fun <reified T : Any> kotlinArgumentCaptor(): KotlinArgumentCaptor<T> = 118 KotlinArgumentCaptor(T::class.java) 119 120 /** 121 * Helper function for creating and using a single-use ArgumentCaptor in kotlin. 122 * 123 * val captor = argumentCaptor<Foo>() 124 * verify(...).someMethod(captor.capture()) 125 * val captured = captor.value 126 * 127 * becomes: 128 * 129 * val captured = withArgCaptor<Foo> { verify(...).someMethod(capture()) } 130 * 131 * NOTE: this uses the KotlinArgumentCaptor to avoid the NullPointerException. 132 */ 133 inline fun <reified T : Any> withArgCaptor(block: KotlinArgumentCaptor<T>.() -> Unit): T = 134 kotlinArgumentCaptor<T>().apply { block() }.value 135 136 /** 137 * Variant of [withArgCaptor] for capturing multiple arguments. 138 * 139 * val captor = argumentCaptor<Foo>() 140 * verify(...).someMethod(captor.capture()) 141 * val captured: List<Foo> = captor.allValues 142 * 143 * becomes: 144 * 145 * val capturedList = captureMany<Foo> { verify(...).someMethod(capture()) } 146 */ 147 inline fun <reified T : Any> captureMany(block: KotlinArgumentCaptor<T>.() -> Unit): List<T> = 148 kotlinArgumentCaptor<T>().apply{ block() }.allValues 149