- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有一个启动协程的 broadcastReceiver,我正在尝试对其进行单元测试...
广播:
class AlarmBroadcastReceiver: BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
Timber.d("Starting alarm from broadcast receiver")
//inject(context) Don't worry about this, it's mocked out
GlobalScope.launch {
val alarm = getAlarm(intent)
startTriggerActivity(alarm, context)
}
}
private suspend fun getAlarm(intent: Intent?): Alarm {
val alarmId = intent?.getIntExtra(AndroidAlarmService.ALARM_ID_KEY, -1)
if (alarmId == null || alarmId < 0) {
throw RuntimeException("Cannot start an alarm with an invalid ID.")
}
return withContext(Dispatchers.IO) {
alarmRepository.getAlarmById(alarmId)
}
}
下面是测试:
@Test
fun onReceive_ValidAlarm_StartsTriggerActivity() {
val alarm = Alarm().apply { id = 100 }
val intent: Intent = mock {
on { getIntExtra(any(), any()) }.thenReturn(alarm.id)
}
whenever(alarmRepository.getAlarmById(alarm.id)).thenReturn(alarm)
alarmBroadcastReceiver.onReceive(context, intent)
verify(context).startActivity(any())
}
发生的事情是我正在验证的函数从未被调用过。测试在协程返回之前结束...我知道 GlobalScope
不好用,但我不确定该怎么做。
编辑 1:如果我在 verify
之前延迟,它似乎可以工作,因为它允许协程有时间完成并返回,但是,我不想依赖延迟/ sleep 进行测试。 . 我认为解决方案是适本地引入一个范围而不是使用 GlobalScope
并在测试中控制它。 las,我不知道声明协程作用域的约定是什么。
最佳答案
我明白了,你必须使用 Unconfined
调度员:
val Unconfined: CoroutineDispatcher (source)
A coroutine dispatcher that is not confined to any specific thread. It executes the initial continuation of a coroutine in the current call-frame and lets the coroutine resume in whatever thread that is used by the corresponding suspending function, without mandating any specific threading policy. Nested coroutines launched in this dispatcher form an event-loop to avoid stack overflows.
Documentation sample:
withContext(Dispatcher.Unconfined) {
println(1)
withContext(Dispatcher.Unconfined) { // Nested unconfined
println(2)
}
println(3)
}
println("Done")
对于我的 ViewModel 测试,我将协程上下文传递给 ViewModel 构造函数,以便我可以在 Unconfined
和其他调度程序之间切换,例如Dispatchers.Main
和 Dispatchers.IO
。
测试的协程上下文:
@ExperimentalCoroutinesApi
class TestContextProvider : CoroutineContextProvider() {
override val Main: CoroutineContext = Unconfined
override val IO: CoroutineContext = Unconfined
}
实际 ViewModel 实现的协程上下文:
open class CoroutineContextProvider {
open val Main: CoroutineContext by lazy { Dispatchers.Main }
open val IO: CoroutineContext by lazy { Dispatchers.IO }
}
View 模型:
@OpenForTesting
class SampleViewModel @Inject constructor(
val coroutineContextProvider: CoroutineContextProvider
) : ViewModel(), CoroutineScope {
private val job = Job()
override val coroutineContext: CoroutineContext = job + coroutineContextProvider.Main
override fun onCleared() = job.cancel()
fun fetchData() {
launch {
val response = withContext(coroutineContextProvider.IO) {
repository.fetchData()
}
}
}
}
从协程核心版本 1.2.1
开始,您可以使用 runBlockingTest
:
依赖关系:
def coroutines_version = "1.2.1"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version"
例如:
@Test
fun `sendViewState() sends displayError`(): Unit = runBlockingTest {
Dispatchers.setMain(Dispatchers.Unconfined)
val apiResponse = ApiResponse.success(data)
whenever(repository.fetchData()).thenReturn(apiResponse)
viewModel.viewState.observeForever(observer)
viewModel.processData()
verify(observer).onChanged(expectedViewStateSubmitError)
}
关于Android Kotlin 协程单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56531647/
在我的设置中,我试图有一个界面 Table继承自 Map (因为它主要用作 map 的包装器)。两个类继承自 Table - 本地和全局。全局的将有一个可变的映射,而本地的将有一个只有本地条目的映射。
Rust Nomicon 有 an entire section on variance除了关于 Box 的这一小节,我或多或少地理解了这一点和 Vec在 T 上(共同)变体. Box and Vec
我是一名优秀的程序员,十分优秀!