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>
}
Yes, you should put the dataRepository.getCharacters()
call in a runBlocking
.
And the verify
should be replaced for coVerify
.
In the end, the test should look like this:
@Test
fun getCharacters() {
val respose = runBlocking { dataRepository.getCharacters() }
coVerify { apiServiceInterface.getDataFromApi() }
}
Also, since you want to verify it has happened only once, you need to call coVerify
with the exactly parameter coVerify(exactly = 1)
I think you should prefer using runTest
instead of runBlocking
or runBlockingTest
.To tell you in brief about the three.
runBlocking
allows you to call suspend functions by blocking a new coroutine and it blocks the current thread until it is completed.
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 .
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.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.