I have a repository that creates a flow where I emit the result of a suspending Retrofit method. This works in the app, but I would like to run tests on the code.
I am using kotlinx-coroutines-test v1.6.0 and MockWebServer v4.9.3 in my tests. When I try to run a test, I get:
Timed out waiting for 1000 ms
kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 1000 ms
at app//kotlinx.coroutines.TimeoutKt.TimeoutCancellationException(Timeout.kt:184)
at app//kotlinx.coroutines.TimeoutCoroutine.run(Timeout.kt:154)
at app//kotlinx.coroutines.test.TestDispatcher.processEvent$kotlinx_coroutines_test(TestDispatcher.kt:23)
at app//kotlinx.coroutines.test.TestCoroutineScheduler.tryRunNextTask(TestCoroutineScheduler.kt:95)
at app//kotlinx.coroutines.test.TestCoroutineScheduler.advanceUntilIdle(TestCoroutineScheduler.kt:110)
at app//kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt.runTestCoroutine(TestBuilders.kt:212)
at app//kotlinx.coroutines.test.TestBuildersKt.runTestCoroutine(Unknown Source)
at app//kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTest$1$1.invokeSuspend(TestBuilders.kt:167)
at app//kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTest$1$1.invoke(TestBuilders.kt)
at app//kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTest$1$1.invoke(TestBuilders.kt)
at app//kotlinx.coroutines.test.TestBuildersJvmKt$createTestResult$1.invokeSuspend(TestBuildersJvm.kt:13)
(Coroutine boundary)
at app.cash.turbine.ChannelBasedFlowTurbine$awaitEvent$2.invokeSuspend(FlowTurbine.kt:247)
at app.cash.turbine.ChannelBasedFlowTurbine$withTimeout$2.invokeSuspend(FlowTurbine.kt:215)
at app.cash.turbine.ChannelBasedFlowTurbine.awaitItem(FlowTurbine.kt:252)
at ogbe.eva.prompt.home.HomeRepositoryTest$currentTask when server responds with error emits failure$1$1.invokeSuspend(HomeRepositoryTest.kt:90)
at app.cash.turbine.FlowTurbineKt$test$2.invokeSuspend(FlowTurbine.kt:86)
at ogbe.eva.prompt.home.HomeRepositoryTest$currentTask when server responds with error emits failure$1.invokeSuspend(HomeRepositoryTest.kt:89)
at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTestCoroutine$2.invokeSuspend(TestBuilders.kt:208)
(Coroutine creation stacktrace)
at app//kotlinx.coroutines.intrinsics.UndispatchedKt.startCoroutineUndispatched(Undispatched.kt:184)
at app//kotlinx.coroutines.test.TestBuildersJvmKt$createTestResult$1.invokeSuspend(TestBuildersJvm.kt:13)
at app//kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at app//kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at app//kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt.runTest$default(TestBuilders.kt:161)
at app//kotlinx.coroutines.test.TestBuildersKt.runTest$default(Unknown Source)
at app//ogbe.eva.prompt.TestCoroutineRule.runTest(TestCoroutineRule.kt:26)
at app//ogbe.eva.prompt.home.HomeRepositoryTest.currentTask when server responds with error emits failure(HomeRepositoryTest.kt:84)
at java.base@11.0.11/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base@11.0.11/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base@11.0.11/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base@11.0.11/java.lang.reflect.Method.invoke(Method.java:566)
at app//org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at app//org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at app//org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at app//org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at app//org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at app//org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at app//org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:61)
at app//org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at app//org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at app//org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at app//org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at app//org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at app//org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at app//org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at app//org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at app//org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at app//org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at app//org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at app//org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:110)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:38)
at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:62)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
at java.base@11.0.11/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base@11.0.11/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base@11.0.11/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base@11.0.11/java.lang.reflect.Method.invoke(Method.java:566)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker$2.run(TestWorker.java:176)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:133)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71)
at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
Caused by: kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 1000 ms
at app//kotlinx.coroutines.TimeoutKt.TimeoutCancellationException(Timeout.kt:184)
at app//kotlinx.coroutines.TimeoutCoroutine.run(Timeout.kt:154)
at app//kotlinx.coroutines.test.TestDispatcher.processEvent$kotlinx_coroutines_test(TestDispatcher.kt:23)
at app//kotlinx.coroutines.test.TestCoroutineScheduler.tryRunNextTask(TestCoroutineScheduler.kt:95)
at app//kotlinx.coroutines.test.TestCoroutineScheduler.advanceUntilIdle(TestCoroutineScheduler.kt:110)
at app//kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt.runTestCoroutine(TestBuilders.kt:212)
at app//kotlinx.coroutines.test.TestBuildersKt.runTestCoroutine(Unknown Source)
at app//kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTest$1$1.invokeSuspend(TestBuilders.kt:167)
at app//kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTest$1$1.invoke(TestBuilders.kt)
at app//kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTest$1$1.invoke(TestBuilders.kt)
at app//kotlinx.coroutines.test.TestBuildersJvmKt$createTestResult$1.invokeSuspend(TestBuildersJvm.kt:13)
at app//kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at app//kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at app//kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:279)
at app//kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
at app//kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
at app//kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
at app//kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
at app//kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
at app//kotlinx.coroutines.test.TestBuildersJvmKt.createTestResult(TestBuildersJvm.kt:12)
at app//kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt.runTest(TestBuilders.kt:166)
at app//kotlinx.coroutines.test.TestBuildersKt.runTest(Unknown Source)
... 50 more
I don't get the chance to run my test assertions, which is what I want to do. It just fails with this unexpected error.
I have checked with calling random suspending functions in the flow and running the mock server outside of my flow function. Both of those will complete without the timeout error, but when I combine the flow and the test and Retrofit, it shows the timeout error.
Repository code:
class HomeRepository @Inject constructor(
@IoDispatcher ioDispatcher: CoroutineDispatcher,
private val promptService: PromptService,
) {
val currentTask = flow {
try {
val response = promptService.getSchedule(1) // Suspending Retrofit method that fails the tests
if (response.isSuccessful) {
val schedule = response.body()
if (schedule == null) {
Log.e(TAG, "Get schedule response has empty body")
emit(LoadState.Failure())
} else {
emit(LoadState.Data(schedule.tasks.first()))
}
} else {
Log.e(
TAG,
"Server responded to get schedule request with error: ${response.message()}"
)
emit(LoadState.Failure())
}
} catch (e: Exception) {
Log.e(TAG, "Could not get schedule from server", e)
emit(LoadState.Failure())
}
}
.flowOn(ioDispatcher)
companion object {
private val TAG = HomeRepository::class.simpleName
}
}
Test code:
@OptIn(ExperimentalCoroutinesApi::class)
class HomeRepositoryTest {
@get:Rule
val testCoroutineRule = TestCoroutineRule()
private val mockWebServer = MockWebServer()
@Before
fun setUp() {
mockWebServer.start()
}
@After
fun tearDown() {
mockWebServer.shutdown()
}
@Test
fun `currentTask when server responds with error emits failure`() = testCoroutineRule.runTest {
mockWebServer.enqueue(MockResponse().setResponseCode(500))
val homeRepository = createRepository()
homeRepository.currentTask.test {
expectThat(awaitItem()).isFailure()
awaitComplete()
}
}
private fun createRepository(promptService: PromptService = createPromptService()) =
HomeRepository(testCoroutineRule.testDispatcher, promptService)
private fun createPromptService(): PromptService {
val client = OkHttpClient.Builder()
.connectTimeout(1, TimeUnit.SECONDS)
.readTimeout(1, TimeUnit.SECONDS)
.writeTimeout(1, TimeUnit.SECONDS)
.build()
return Retrofit.Builder()
.baseUrl(mockWebServer.url("/"))
.client(client)
.addConverterFactory(MoshiConverterFactory.create())
.build()
.create(PromptService::class.java)
}
}
Rule code:
@OptIn(ExperimentalCoroutinesApi::class)
class TestCoroutineRule : TestWatcher() {
val testDispatcher = StandardTestDispatcher()
private val testScope = TestScope(testDispatcher)
override fun starting(description: Description?) {
super.starting(description)
Dispatchers.setMain(testDispatcher)
}
override fun finished(description: Description?) {
super.finished(description)
Dispatchers.resetMain()
}
fun runTest(block: suspend TestScope.() -> Unit) =
testScope.runTest(testBody = block)
}
How do I test a flow that uses Retrofit without getting a timeout error?
I ran into the same issue. I figured out all this was because of kotlin-coroutines-test 1.6.0 and more specifically the runTest behavior.
runTest allows you to control the virtual time (like runBlockingTest) which is convenient when testing. But your issue here is that you are using Retrofit with OkHttp (MockWebServer in tests) which is running inside its own thread and is using real time outside of the test dispatcher.
Here the solution would be to either
@Test
fun `my unit test`() = runTest {
withContext(Dispatchers.Default) { // can be either Dispatchers.Default or Dispatchers.IO but not Dispatchers.Main
// enter code here
}
}
Another solution would be to provide a different thread context to the Main dispatcher by creating a property in your test class like:
@OptIn(DelicateCoroutinesApi::class)
private val mainThreadSurrogate = newSingleThreadContext("UI thread")
@Before
fun setUp() {
Dispatchers.setMain(mainThreadSurrogate)
}
@After
fun tearDown() {
Dispatchers.resetMain()
mainThreadSurrogate.close()
}
@Test
fun `my unit test`() = runTest {
withContext(Dispatchers.Main) { // this is not mandatory here as the default dispatcher is Main
// enter code here
}
}
I am pretty new to coroutines and coroutines testing specifically so I may have misunderstood some details but I hope it helped somehow.
The documentation can also be handy: https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-test/README.md
I still haven't figured out how to directly test the currentTask
flow property. But I did find a workaround that lets me test most of the functionality.
I split off a base repository that handles most of the logic without referencing Retrofit. I also split off a suspending method from the property flow, so that I can test the Retrofit logic without going through a flow.
Base repository:
abstract class BaseLoadingRepository(private val ioDispatcher: CoroutineDispatcher) {
protected suspend fun <NetworkResponse : Any, DbResponse : Any> loadFromServer(
callNetwork: suspend () -> Response<NetworkResponse>,
mapResponse: (NetworkResponse) -> DbResponse?,
saveData: suspend (DbResponse?) -> Unit,
staleData: DbResponse? = null,
) = withContext(ioDispatcher) {
try {
val response = callNetwork()
if (response.isSuccessful) {
val body = response.body()
if (body == null) {
Log.e(TAG, "Server response has empty body")
LoadState.Failure(staleData)
} else {
val dbData = mapResponse(body)
saveData(dbData)
LoadState.toContent(dbData)
}
} else {
Log.e(
TAG,
"Server responded with error: ${response.message()}"
)
LoadState.Failure(staleData)
}
} catch (e: Exception) {
Log.e(TAG, "Could not load data from server", e)
LoadState.Failure(staleData)
}
}
protected fun <T : Any> getLoadedFlow(
getFromDb: () -> Flow<T?>,
getFromNetwork: suspend (T?) -> Unit,
canEmitCache: (T) -> Boolean = { true },
) = flow {
try {
val cachedData = getFromDb().firstOrNull()
cachedData?.let {
if (canEmitCache(cachedData)) {
emit(LoadState.Data(cachedData))
}
}
getFromNetwork(cachedData)
val refreshedData = getFromDb().map(LoadState.Companion::toContent)
emitAll(refreshedData)
} catch (e: Exception) {
Log.e(TAG, "Could not load data", e)
emit(LoadState.Failure())
}
}.flowOn(ioDispatcher)
companion object {
private val TAG = BaseLoadingRepository::class.simpleName
}
}
Concrete repository:
class HomeRepository @Inject constructor(
@IoDispatcher ioDispatcher: CoroutineDispatcher,
private val promptDatabase: PromptDatabase,
private val promptService: PromptService,
) : BaseLoadingRepository(ioDispatcher) {
val currentTask =
getLoadedFlow<Task>({ promptDatabase.taskDao().getCurrentTask() }, this::loadCurrentTask)
@VisibleForTesting
internal suspend fun loadCurrentTask(cachedData: Task? = null) =
loadFromServer(
{ promptService.getSchedule(1) },
{
it.tasks.firstOrNull()?.toTask(0)
},
{ task ->
promptDatabase.withTransaction {
promptDatabase.taskDao().clearPositions()
task?.let { promptDatabase.taskDao().insert(it) }
}
},
cachedData
)
}
Base repository test:
@OptIn(ExperimentalCoroutinesApi::class)
class BaseLoadingRepositoryTest {
@MockK
lateinit var response: Response<String>
private val testDispatcher = StandardTestDispatcher()
private val testScope = TestScope(testDispatcher)
@Before
fun setUp() {
MockKAnnotations.init(this)
}
@Test
fun `loadFromServer with network response body returns mapped data`() = testScope.runTest {
every { response.isSuccessful } returns true
every { response.body() } returns "2"
val testRepository = createRepository()
val result = testRepository.publicLoadFromServer({ response }, String::toInt, {})
expectThat(result).data.isEqualTo(2)
}
@Test
fun `loadFromServer with network response body saves data in database`() = testScope.runTest {
val saveData = mockk<(Int?) -> Unit>(relaxed = true)
every { response.isSuccessful } returns true
every { response.body() } returns "2"
val testRepository = createRepository()
testRepository.publicLoadFromServer({ response }, String::toInt, saveData)
verify { saveData(2) }
}
@Test
fun `loadFromServer without network response body returns failure`() = testScope.runTest {
val staleData = 2
every { response.isSuccessful } returns true
every { response.body() } returns null
val testRepository = createRepository()
val result = testRepository.publicLoadFromServer({ response }, String::toInt, {}, staleData)
expectThat(result).failureData.isEqualTo(staleData)
}
@Test
fun `loadFromServer when network responds with error returns failure`() = testScope.runTest {
val staleData = 2
every { response.isSuccessful } returns false
every { response.message() } returns "Internal Server Error"
val testRepository = createRepository()
val result = testRepository.publicLoadFromServer({ response }, String::toInt, {}, staleData)
expectThat(result).failureData.isEqualTo(staleData)
}
@Test
fun `loadFromServer when throws returns failure`() = testScope.runTest {
val staleData = 2
val testRepository = createRepository()
val result = testRepository.publicLoadFromServer(
{ throw RuntimeException("Oh no!") },
String::toInt,
{},
staleData
)
expectThat(result).failureData.isEqualTo(staleData)
}
@Test
fun `getLoadedFlow emits refreshed data`() = testScope.runTest {
val getFromDb = mockk<() -> Flow<Int?>>(relaxed = true)
every { getFromDb() } returns emptyFlow() andThen flowOf(2)
val testRepository = createRepository()
val result = testRepository.publicGetLoadedFlow(getFromDb, {}, { false })
result.test {
expectThat(awaitItem()).data.isEqualTo(2)
awaitComplete()
}
}
@Test
fun `getLoadedFlow loads from the network`() = testScope.runTest {
val getFromDb = mockk<() -> Flow<Int?>>(relaxed = true)
val getFromNetwork = mockk<(Int?) -> Unit>(relaxed = true)
every { getFromDb() } returns flowOf(2) andThen flowOf(3)
val testRepository = createRepository()
val result = testRepository.publicGetLoadedFlow(getFromDb, getFromNetwork) { false }
result.test {
expectThat(awaitItem()).data.isEqualTo(3)
awaitComplete()
verify { getFromNetwork(2) }
}
}
@Test
fun `getLoadedFlow when can emit cached data emits cached data`() = testScope.runTest {
val getFromDb = mockk<() -> Flow<Int?>>(relaxed = true)
every { getFromDb() } returns flowOf(2) andThen flowOf(3)
val testRepository = createRepository()
val result = testRepository.publicGetLoadedFlow(getFromDb, {}, { true })
result.test {
expectThat(awaitItem()).data.isEqualTo(2)
expectThat(awaitItem()).data.isEqualTo(3)
awaitComplete()
}
}
@Test
fun `getLoadedFlow when cached data null does not emit cached data`() = testScope.runTest {
val getFromDb = mockk<() -> Flow<Int?>>(relaxed = true)
every { getFromDb() } returns flowOf(null) andThen flowOf(2)
val testRepository = createRepository()
val result = testRepository.publicGetLoadedFlow(getFromDb, {}, { true })
result.test {
expectThat(awaitItem()).data.isEqualTo(2)
awaitComplete()
}
}
@Test
fun `getLoadedFlow when throws emits failure`() = testScope.runTest {
val testRepository = createRepository()
val result = testRepository.publicGetLoadedFlow<Any>(
{ throw RuntimeException("Oh no!") },
{},
{ false }
)
result.test {
expectThat(awaitItem()).isFailure()
awaitComplete()
}
}
private fun createRepository() = TestRepository(testDispatcher)
private class TestRepository(testDispatcher: CoroutineDispatcher) :
BaseLoadingRepository(testDispatcher) {
suspend fun <NetworkResponse : Any, DbResponse : Any> publicLoadFromServer(
callNetwork: suspend () -> Response<NetworkResponse>,
mapResponse: (NetworkResponse) -> DbResponse?,
saveData: suspend (DbResponse?) -> Unit,
staleData: DbResponse? = null
) = loadFromServer(callNetwork, mapResponse, saveData, staleData)
fun <T : Any> publicGetLoadedFlow(
getFromDb: () -> Flow<T?>,
getFromNetwork: suspend (T?) -> Unit,
canEmitCache: (T) -> Boolean,
) = getLoadedFlow(getFromDb, getFromNetwork, canEmitCache)
}
}
Concrete repository test:
@OptIn(ExperimentalCoroutinesApi::class)
class HomeRepositoryTest {
private val promptDatabase = createPromptDatabase()
private val mockWebServer = MockWebServer()
private val testDispatcher = StandardTestDispatcher()
private val testScope = TestScope(testDispatcher)
@Before
fun setUp() {
mockWebServer.start()
mockWebServer.enqueueFile("get_current_task_response.json")
}
@After
fun tearDown() {
mockWebServer.shutdown()
}
@Test
fun loadCurrentTask_returnsCurrentTask() = testScope.runTest {
val homeRepository = createRepository()
val result = homeRepository.loadCurrentTask()
expectThat(result).data.isEqualTo(TestData.CurrentTask)
}
@Test
fun loadCurrentTask_loadsCurrentTaskNetwork() = testScope.runTest {
val homeRepository = createRepository()
homeRepository.loadCurrentTask()
runCatching {
val request = mockWebServer.takeRequest(1, TimeUnit.SECONDS)
expectThat(request).hasRequestLine("GET /schedule?size=1")
}
}
@Test
fun loadCurrentTask_insertsCurrentTaskInDatabase() = testScope.runTest {
val homeRepository = createRepository()
homeRepository.loadCurrentTask()
val schedule = promptDatabase.taskDao().getSchedule().first()
expectThat(schedule).containsExactly(TestData.CurrentTask)
}
private fun createRepository(): HomeRepository {
val promptService = createPromptService(mockWebServer.url("/"))
return HomeRepository(testDispatcher, promptDatabase, promptService)
}
}
This gets me 90% of the way there. It would still be nice to directly test the currentTask
property since there is a little bit of logic still left in there.
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.