I'm trying to test that Permission.REVEAL_NOW returns the method isFeatureRevealNowAvailable.
Heres some code I already tried but didn't succeed with. Any help would be hugely appreciated as always!
Function to test:
class PermissionRepository(private val permissionApi: PermissionApi,
private val appPreferences: AppPreferences) {
fun checkPermission(permission: PermissionType, onPermissionResponse: (Boolean) -> Unit) {
Log.i("Permission", "Checking permission")
when (permission) {
PermissionType.REVEAL_NOW -> {
isFeatureRevealNowAvailable(onPermissionResponse, ::errorHandler)
}
}
}
Attempted solution:
#RunWith(MockitoJUnitRunner::class)
class PermissionRepositoryTest{
#Test
fun checkPermissionTest() {
val mockPermissionRepository = mock(PermissionRepository::class.java)
val mockPermissionApi = mock(PermissionApi::class.java)
val result = mockPermissionRepository.checkPermission(PermissionType.REVEAL_NOW, onPermissionResponse = null)
//Unsure of what to use here AssertThat or Mockito's "when" function
}
}
private fun isFeatureRevealNowAvailable(permissionResponseHandler: (Boolean) -> Unit, permissionError: (Throwable) -> Unit) {
permissionApi.getRevealNowPermission().enqueue(object : Callback<PermissionResponse> {
override fun onFailure(call: Call<PermissionResponse>, t: Throwable) {
permissionResponseHandler(false)
permissionError(t)
}
override fun onResponse(call: Call<PermissionResponse>, response: Response<PermissionResponse>) {
val permissionResult = response.body()?.isRevealNow ?: false
updateUserLocalPermission(PermissionType.REVEAL_NOW, permissionResult)
permissionResponseHandler(permissionResult)
}
})
}
(TL;DR - go to Example)
Since you don't define specifically what you want to test or achieve, I will give some overall tips:
Mocking
Never mock the class you want to test
val mockPermissionRepository = mock(PermissionRepository::class.java)
Mock only what you want to exclude from your test but rely on
Try to avoid mocks whenever it is possible and makes sence, because they simulate a perfect world, not the real behaviour
Test naming
Use proper names to describe what you want to achive with your test. My personal favourite is to start the sentence with the word "should ..."
Example
Two tests I could image to write for your function:
#Test
fun `should invoke reveal permission on PermissionApi when type is REVEAL_NOW`(){
val mockPermissionApi = mock(PermissionApi::class.java)
val permissionRepository = PermissionRepository(mockPermissionApi, mock())
permissionRepository.checkPermission(PermissionType.REVEAL_NOW, onPermissionResponse = {})
verify(mockPermissionApi, times(1)).getRevealNowPermission()
}
#Test
fun `should do nothing when type is not REVEAL_NOW`() {
val mockPermissionApi = mock(PermissionApi::class.java)
val permissionRepository = PermissionRepository(mockPermissionApi, mock())
permissionRepository.checkPermission(PermissionType.ELSE, onPermissionResponse = {})
verify(mockPermissionApi, times(0)).getRevealNowPermission()
}
Related
I am trying to verify that .shuffled() on a list is called, but get an error on running because of a prior .take(6) call on the list, and I cannot see a way around this.
Here is some code that gets the same error:
val mockList =
mockk<List<String>> { every { shuffled() } returns mockk(relaxed = true) }
val choiceList = spyk(listOf("String1", "String2")) { every { take(6) } returns mockList }
val tmp = choiceList.take(6)
val tmp2 = tmp.shuffled()
verify {mockList.shuffled())
On line 4, I get the following error:
class io.mockk.renamed.java.util.List$Subclass0 cannot be cast to class java.lang.Integer (io.mockk.renamed.java.util.List$Subclass0 is in unnamed module of loader 'app'; java.lang.Integer is in module java.base of loader 'bootstrap')
Attempting to go around by directly verifying on choiceList.take(6).shuffled() and combining the two tmp vals into one has had no success, as it gets true whether or not .shuffled() gets called. Also, switching from a spy to a mock for choiceList has also not worked.
Edit: Note, since this is a toy example, the take() is completely necessary, and cannot be removed, as it has real use in the actual code.
Interesting one!
I think in current implementation it is not possible. The easy answer would be "this test misses the declaration of wrapping static class" (as extension methods are just the same as java static methods for JVM). But if we add it...
#Test
fun test() {
mockkStatic("kotlin.reflect.jvm.internal.impl.utils.CollectionsKt")
val iterClass = mockkClass(Iterable::class)
val mockList = mockk<List<String>> { every { shuffled() } returns mockk(relaxed = true) }
with(iterClass) {
every { take(6) } returns mockList
val tmp = take(6)
val tmp2 = tmp.shuffled()
verify {
mockList.shuffled()
}
}
}
we have a Recursion detected in a lazy value under LockBasedStorageManager#1d2ad266 (DeserializationComponentsForJava.ModuleData) which is understandable - we just mocked the whole extensions package. And it is not possible to mock only one extension method leaving others intact. (source: https://github.com/mockk/mockk#extension-functions)
However, I'd do the following. Why not make our own extension functions which call the original and mock those?
It would go like this:
Main.kt:
package root
...
fun <T> Iterable<T>.take(n: Int): Iterable<T> {
val m = Iterable<T>::take
return m.call(this)
}
fun <T> Iterable<T>.shuffled(): Iterable<T> {
val m = Iterable<T>::shuffled
return m.call(this)
}
Test.kt:
package root
...
#Test
fun test() {
// note this changed
mockkStatic("root.MainKt")
val iterClass = mockkClass(Iterable::class)
val mockList = mockk<List<String>> { every { shuffled() } returns mockk(relaxed = true) }
with(iterClass) {
every { take(6) } returns mockList
val tmp = take(6)
val tmp2 = tmp.shuffled()
verify {
mockList.shuffled()
}
}
}
The only downside here I think is that it's reflection (duh!) So, this can possibly affect performance and has the requirement to have implementation(kotlin("reflect")) in the dependencies (to use call()). If it is not feasible I think there's no clean solution.
val mockList: List<String> = mockk(relaxed = true)
mockList.shuffled()
verify { mockList.shuffled() }
This works for me. The problem is that take of choiceList cannot be mocked somehow. Is that really necessary?
everyone!
I know how to unit test private fun with reflection:
#Test
fun testFun() {
val method = myService.javaClass.getDeclaredMethod("getSomeThing", SomethingDto::class.java)
method.isAccessible = true
val somethingDto = SomethingDto(id = UUID.randomUUID())
val parameters = arrayOfNulls<Any>(1)
parameters[0] = somethingDto
method.invoke(myService,*parameters)
}
And, for testing functions, returning Mono, I use .block():
#Test
fun testCreate() {
val somethingDto = SomethingDto(id = UUID.randomUUID())
myService.create(somethingDto).block()
}
But if I need to test private fun, like in 1st example, but fun returns Mono, how can I do it?
This doesn't work
method.invoke(myService,*parameters).block()
and
method.invoke(myService,*parameters).toMono().block()
grateful for your help
I'm trying to write some unit tests for the following Kotlin code which uses async to create coroutines:
class ClassToTest(val dependency1: Dependency1, val dependency2: Dependency2) {
fun funToTest(list: List<X>) {
runBlocking {
val deferreds: List<Deferred<Type>> = list.map { item ->
async(Dispatchers.Default) {
val v1 = dependency1.func1(item)
val v2 = dependency2.func2(v1)
someFunc(v2)
}
}
val results = deferreds.awaitAll()
}
}
}
I mocked both dependency1 and dependency2 in the unit testing and provided a list of 2 items as input to the funToTest. I also mocked how the dependencies should return values based on different input, something like below
val slot1 = slot<>()
every {dependency1.func1(capture(slot1))} answers {
if (slot1.captured.field == xxx) {
return1
} else {
return2
}
}
//similar thing for dependency1
// invoke funToTest
However this doesn't seem like working as I'm getting unexpected results which indicated the mocked object didn't return results as desired between two coroutines.
Does anyone have any ideas what went wrong with these code?
I'm new in Unit Testing, I can't understand how to test this kind of method with kotlin, using MockK:
override fun register(firebaseId: String, uniqueId: String): Completable {
return Observable.just(0).observeOn(schedulerProvider.io()).flatMap {
val requestRegisterPushes = registerBuilder.build(firebaseId, uniqueId)
apiServiceFactory.build()
.registerPushes(requestRegisterPushes)
.subscribeOn(schedulerProvider.io())
.observeOn(schedulerProvider.ui())
}.flatMapCompletable {
Completable.complete()
}
}
This is my code for the test, the test was a success but the condition coverage does not increase.
#Test
fun `register_Test()`() {
val requestRegisterPushes = mockk<RequestRegisterPushes>(relaxed = true)
every { registerBuilder.build(any(), any(), any(), any()) } returns requestRegisterPushes
every { apiServiceFactory.build().register(requestRegisterPushes) } returns Observable.just(SimpleResponse())
val resp = userRepository.register("x7gbyb68837g78s", "XXX-XXX-XXX")
}
I would really appreciate it if you could help me a little.
Of course you don't have any coverage because code inside flatmap and flatMapCompletable operator was not executed.
You need to subscribe to Observable to make it emmit elements in you case it will emmit 0 only when you subscribe for it. That's how RxJava works. Something like this:
val subscriber = TestSubscriber<>();
val resp = userRepository.register("x7gbyb68837g78s", "XXX-XXX-XXX").subscribe(subscriber)
subscriber.assertComplete()
I have a test here, where I am attempting to validate what is being sent to a ConflatedBroadcastChannel (experimental API, I know):
#Test
fun myTest() = runBlockingTest {
val results = ArrayList<String>()
val myChannel = ConflatedBroadcastChannel<String>()
myChannel.openSubscription().consumeEach {
results.add(it)
}
myChannel.send("hello")
assertEquals(1, results.size)
}
But when I run that code, I receive an IllegalStateException with the messge: "This job has not completed yet". Anyone know what's missing? I've tried using TestCoroutineDispatcher as well to no avail.
If you just want to obtain a single value from a channel you can always use receive.
Since ConflatedBroadcastChannel caches the latest value, you can just use it like this:
#Test
fun myTest() = runBlockingTest {
val myChannel = ConflatedBroadcastChannel<String>()
myChannel.send("hello")
val subscription = myChannel.openSubscription()
val result = subscription.receive()
subscription.cancel()
assertEquals("hello", result)
}
So I thought about the message I was getting, and suspected maybe that the "job that's not completed yet" was the .consumeEach operation I was performing. So to be explicit, I wrapped the operation in a launch(myTestCoroutineDispatcher) and called cancel. This doesn't seem like the ideal way to test things, but it makes sense given that the test environment isn't really guessing when your channel should stop receiving things. Anyways, lo and behold, this works:
#Test
fun myTest() = runBlockingTest {
val results = ArrayList<String>()
val myChannel = ConflatedBroadcastChannel<String>()
launch(testMainDispatcher) {
myChannel.openSubscription().consumeEach {
results.add(it)
cancel()
}
}
myChannel.send("hello")
assertEquals(1, results.size)
}
EDIT: another thing you can do to achieve same results is val myJob = launch {...} and then at the end of your test execution, call myJob.cancel() for the same effect.