我有一个存储库,它创建一个流,在其中我发出挂起Retrofit方法的结果。这在应用程序中是可行的,但是我想对代码进行测试。
我在测试中使用kotlinx test v1.6.0和MockWebServer v4.9.3。当我试着做测试时,我得到:
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
我没有机会运行我的测试断言,这正是我想要做的。它只是失败了这个意外的错误。
我在流中调用了随机挂起函数,并在我的flow函数之外运行了模拟服务器。这两种方法都将在没有超时错误的情况下完成,但是当我将流、测试和重定向组合在一起时,就会显示超时错误。
存储库代码:
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
}
}
测试代码:
@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)
}
}
规则代码:
@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)
}
如何测试使用Retrofit而不出现超时错误的流?
发布于 2022-04-11 17:54:02
我遇到了同样的问题。我发现这一切都是因为kotlin-coroutines test 1.6.0,更具体地说,是因为runTest行为。
runTest允许您控制虚拟时间(如runBlockingTest),这在测试时非常方便。但是这里的问题是,您使用的是OkHttp (MockWebServer in test ),它运行在它自己的线程中,并且在测试调度程序之外使用实时。
这里的解决办法是
如果不需要控制虚拟时间,例如调用(),则使用runBlocking代替runTest,例如,通过用"withContext(Dispatchers.Default)“或”runTest“包装测试内容,在另一个调度程序上运行测试。
@Test
fun `my unit test`() = runTest {
withContext(Dispatchers.Default) { // can be either Dispatchers.Default or Dispatchers.IO but not Dispatchers.Main
// enter code here
}
}
另一种解决方案是通过在测试类中创建一个属性,为主调度程序提供一个不同的线程上下文,如下所示:
@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
}
}
我对协同测试和协同测试非常陌生,所以我可能误解了一些细节,但我希望它能有所帮助。
文档也可以方便使用:https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-test/README.md
发布于 2022-04-10 15:57:00
我还没有弄清楚如何直接测试currentTask
流属性。但我确实找到了一个解决办法,可以让我测试大部分功能。
我拆分了一个基本存储库,它处理大部分逻辑,而不引用Retrofit。我还从属性流中分离出一个挂起方法,这样我就可以在不经历流的情况下测试Retrofit逻辑。
基本储存库:
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
}
}
具体储存库:
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
)
}
基本存储库测试:
@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)
}
}
混凝土储存库测试:
@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)
}
}
这让我90%的时间都能到达那里。直接测试currentTask
属性仍然是件好事,因为那里仍然保留了一些逻辑。
https://stackoverflow.com/questions/71731166
复制相似问题