How to unit test ConflatedBroadcastChannel? - unit-testing

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.

Related

Kotlin Mockk : Unable to mock a list correctly

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?

Verify that suspend function has not returned a value after simulating some time

I am trying to validate that a suspend function does not return anything at all in a certain test.
Consider the following situation:
val completionSignal = Channel<Unit>(capacity = 1, onBufferOverflow = BufferOverflow.DROP_LATEST)
suspend fun waitForCompletionSignal(): String {
completionSignal.receive()
return "Completion signal received"
}
I want to test this code with 2 unit tests, one that validates it returns the string when I provide the CompletionSignal with a value (thats the easy one).
And one that validates that it does not return anything when i don't give it anything. This is the hard one, since how long should I wait? And can i be sure the test fails if my code changes and suddenly the string is returned?
I got the following approach but I am missing some pieces:
#Test
fun `waitForCompletionSignal when completionSignal is provided assert result`() = runTest {
// Start waiting for result
val result = async { waitForCompletionSignal() }
// Provide completion signal
completionSignal.trySend(Unit)
// Await result and verify its correct
assertThat(result.await() == "Completion signal received")
}
#Test
fun `waitForCompletionSignal when completionSignal is not provided assert no result`() = runTest {
// Start waiting for result
val result = async { waitForCompletionSignal() }
// TODO?? some validation that succeeds if the test is just like this, but fails when i do the following:
completionSignal.trySend(Unit)
// A regular await would wait indefinately, and checking if the deferred result is completed does not work very well as well.
}
I hope the question is clear, thanks in advance.
I made an extension function on the deferred type to be able to wait for a max amount of time and after that it will return null. In my particular situation a delay time of 0 (so no delay whatsoever) is enough, but I can imagine that in some situations its useful to delay for a minimum amount of time.
#ExperimentalCoroutinesApi
suspend inline fun <reified T> Deferred<T>.awaitOrNull(
time: Long = 0,
crossinline actBlock: () -> Unit = { }
): T? = coroutineScope {
actBlock()
val timeoutJob = async<T?> {
delay(time)
null
}
return#coroutineScope select<T?> {
this#awaitOrNull.onAwait {
timeoutJob.cancel()
it
}
timeoutJob.onAwait {
this#awaitOrNull.cancel()
it
}
}
}
Using this method i can write the following tests that fail / succeed as expected:
// Succeeds
#Test
fun `waitForCompletionSignal when completionSignal is provided assert result`() = runTest {
val result = async {
waitForCompletionSignal()
}.awaitOrNull {
completionSignal.trySend(Unit)
}
assertThat(result == "Completion signal received")
}
// Succeeds
#Test
fun `waitForCompletionSignal when completionSignal is not provided assert no result`() = runTest {
val result = async {
waitForCompletionSignal()
}.awaitOrNull()
assertThat(result == null)
}
// Fails (to prove that it works)
#Test
fun `waitForCompletionSignal when completionSignal is not provided assert no result`() = runTest {
val result = async {
waitForCompletionSignal()
}.awaitOrNull {
completionSignal.trySend(Unit) // This (obviously) causes the test to fail, as I wanted.
}
assertThat(result == null)
}

Unit test with mockK - flatMap

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()

How can I use Mockito to test this function?

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()
}

Having trouble writing unit test for Kotlin Object

With the logic of TDD in mind and trying to understand how to write unit test's, I am having trouble with Kotlin object. The test pass but I am unsure if this is actually the right test. I am trying to make sure that the Logger.i() method is called and that it saves into the database. But at the moment I am stuck at just its called part.
My Object
object Logger {
fun i(tag: String, msg: String, tr: Throwable? = null): Int {
insertIntoLogDatabase(createLogModel("i", tag, msg, tr))
return if (BuildConfig.DEBUG) Log.i(tag, msg, tr) else 0
}
private fun insertIntoLogDatabase(log: LogModel) {
//Insert into Log DB
logRepo.upsert(log)
}
private fun createLogModel(type: String, tag: String, msg: String, tr: Throwable?) = LogModel(0, type, tag, msg, if (tr != null) tr.message + "\n" + tr?.stackTrace.contentToString() else null)
fun setLogRepo(logRepo: LogRepository) {
this.logRepo = logRepo
}
}
with this, I know that I have to call Logger.setLogRepo(logRemp) to give the Logger access to the repo (and this works)
Where I am stuck is I am trying to unit test the Log.i method call
I have this
#Mock
lateinit var log: Logger
#Before
fun setUp() {
MockitoAnnotations.initMocks(this)
Logger.setLogRepository(logRepo)
}
#Test
fun `log i failed`() {
// When
log.i("Test", "Test1")
// Then
verify(log, times(1)).i("Test", "Test1")
}
I mean this works but is it correct (my gut tells me that something is wrong that I am not actually testing the Logger.i() method
please advise.
Thanks.
As you mentioned, you have to test that when you log the data, it should get stored in the repo in the format you want, and you want to test basically that when you have given the work of sending log to the Logger, it is getting sent to the repository in the correct format and you will assert against that.
So you're test case will look like,
#Mock
lateinit var logRepo: LogRepository
#Before
fun setUp() {
MockitoAnnotations.initMocks(this)
Logger.setLogRepository(logRepo)
}
#Test
fun `when an item is logged, it gets stored in the repository`()
{
val expectedData= <valueThatShouldBeSentToRepo>
Logger.i("Test", "Test1")
verify(logRepo).upsert(expectedData)
}