Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test suspend function using MockK?

I am writing a unit test for my Datarepository layer which simply calls an interface. I am using Kotlin, coroutines and MockK for unit testing. In MockK, how can I verify that I have called apiServiceInterface.getDataFromApi() and has happened only once? Should I put the code in runBlocking?

This is my code:

UnitTest

import com.example.breakingbad.api.ApiServiceInterface
import com.example.breakingbad.data.DataRepository
import io.mockk.impl.annotations.InjectMockKs
import io.mockk.impl.annotations.MockK
import io.mockk.verify
import org.junit.Test

Repository

class DataRepositoryTest {
    @MockK
    private lateinit var apiServiceInterface: ApiServiceInterface

    @InjectMockKs
    private lateinit var dataRepository: DataRepository

    @Test
    fun getCharacters() {
            val respose = dataRepository.getCharacters()
            verify { apiServiceInterface.getDataFromApi() }
    }
}

    class DataRepository @Inject constructor(
    private val apiServiceInterface: ApiServiceInterface
) {
    suspend fun getCharacters(): Result<ArrayList<Character>> = kotlin.runCatching{
        apiServiceInterface.getDataFromApi()
    }
}

Interface

interface ApiServiceInterface {
    @GET("api/characters")
    suspend fun getDataFromApi(): ArrayList<Character>
}
like image 425
BRDroid Avatar asked Dec 08 '25 09:12

BRDroid


1 Answers

I think you should prefer using runTest instead of runBlocking or runBlockingTest.To tell you in brief about the three.

  1. runBlocking allows you to call suspend functions by blocking a new coroutine and it blocks the current thread until it is completed.

  2. runBlockingTest will immediately execute the suspending function skipping past any delay and enter coroutine block immediately unlike runBlocking which will wait for the amount of the delay

Since kotlinx.coroutines 1.6.0 release, runBlockingTest is deprecated in favour of runTest due to these reasons listed in the migration guide.

  1. runTest() will automatically skip calls to delay() and handle uncaught exceptions. Unlike runBlockingTest() , it will wait for asynchronous callbacks to handle situations where some code runs in dispatchers that are not integrated with the test module.

I hope that answers your question of what to choose among these 3 to test your suspending function. You code would look like this -:

@Test
fun getCharacters() = runTest {
    val response = dataRepository.getCharacters()
    
    coVerify { apiServiceInterface.getDataFromApi() }
}

Also note that as David mentioned above, because getDataFromApi() is asynchronous/suspending function as well, you will have to use coVerify instead of verify to mock the same.

like image 175
hiteshchopra11 Avatar answered Dec 09 '25 21:12

hiteshchopra11



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!