I am trying to use mockito in kotlin. I created the following test:
class HeroesDataSourceTest {
@Mock
lateinit var heroesRepository: HeroesRepository
@Mock
lateinit var params: PageKeyedDataSource.LoadInitialParams<Int>
@Mock
lateinit var callback: PageKeyedDataSource.LoadInitialCallback<Int, Heroes.Hero>
val hero = Heroes.Hero(1, "superman", "holasuperman", 1, null, null)
val results = Arrays.asList(hero)
val data = Heroes.Data(results)
val dataResult = Heroes.DataResult(data)
val compositeDisposable = CompositeDisposable()
lateinit var heroesDataSource: HeroesDataSource
private val heroesPublishSubject = PublishSubject.create<Heroes.DataResult>()
@Before
fun initTest(){
MockitoAnnotations.initMocks(this)
}
@Test
fun testLoadInitialSuccess(){
`when`(heroesRepository.getHeroes(ArgumentMatchers.anyInt())).thenReturn(heroesPublishSubject.singleOrError())
heroesDataSource = HeroesDataSource(heroesRepository, compositeDisposable)
val testObserver = TestObserver<Heroes.DataResult>()
heroesDataSource.loadInitial(params, callback)
heroesPublishSubject.onNext(dataResult)
testObserver.assertComplete()
}
}
But when I execute it in the line when(heroesRepository.getHeroes(ArgumentMatchers.anyInt())).thenReturn(heroesPublishSubject.singleOrError())
it just enter to getHeroes method instead of mocking it (and for sure since heroesRepository is not initialized because is mocket the method fails). I use this tons of times in java and it never gave me a single problem. What I have to do in kotlin to mock it properly?
EDIT
Here I put also HeroesRepository class
open class HeroesRepository {
val privateKey = "5009bb73066f50f127907511e70f691cd3f2bb2c"
val publicKey = "51ef4d355f513641b490a80d32503852"
val apiDataSource = DataModule.create()
val pageSize = 20
fun getHeroes(page: Int): Single<Heroes.DataResult> {
val now = Date().time.toString()
val hash = generateHash(now + privateKey + publicKey)
val offset: Int = page * pageSize
return apiDataSource.getHeroes(now, publicKey, hash, offset, pageSize)
}
fun generateHash(variable: String): String {
val md = MessageDigest.getInstance("MD5")
val digested = md.digest(variable.toByteArray())
return digested.joinToString("") {
String.format("%02x", it)
}
}
}
Mockito has been around since the early days of Android development and eventually became the de-facto mocking library for writing unit tests. Mockito and Mockk are written in Java and Kotlin, respectively, and since Kotlin and Java are interoperable, they can exist within the same project.
Mockito verify() method can be used to test number of method invocations too. We can test exact number of times, at least once, at least, at most number of invocation times for a mocked method. We can use verifyNoMoreInteractions() after all the verify() method calls to make sure everything is verified.
@Mock is used to declare/mock the references of the dependent beans, while @InjectMocks is used to mock the bean for which test is being created.
mockk:mockk supports to mock top-level functions in Kotlin. Similar to extension functions, for top-level functions, Kotlin creates a class containing static functions under the hood as well, which in turn can be mocked.
Without adding another dependency, you can replace using the @Mock
annotation with a helper function somewhere:
inline fun <reified T> mock(): T =
Mockito.mock(T::class.java)
// To avoid having to use backticks for "when"
fun <T> whenever(methodCall: T): OngoingStubbing<T> =
Mockito.`when`(methodCall)
Then in your test:
val heroesRepository: HeroesRepository = mock()
@Test
fun yourTest() {
whenever(heroesRepository.getHeroes(ArgumentMatchers.anyInt()))
.thenReturn(heroesPublishSubject.singleOrError())
}
Like you had before. This should work, because you're not expecting Mockito to deal with @Mock lateinit var
, which it seems to be struggling with, and instead instantiating the mock yourself.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With