kotlin - 获取“java.lang.RuntimeException:android.os.Looper 中的方法 getMainLooper 未模拟。” 即使使用 InstantTaskExecutor
问题描述
我正在尝试使用 Livedata 对 ViewModel 进行单元测试,尽管在即时任务执行器的测试类中添加了 JUnit5 扩展,但我得到了:
java.lang.RuntimeException: Method getMainLooper in android.os.Looper not mocked. See http://g.co/androidstudio/not-mocked for details.
at android.os.Looper.getMainLooper(Looper.java)
at androidx.arch.core.executor.DefaultTaskExecutor.isMainThread(DefaultTaskExecutor.java:77)
at androidx.arch.core.executor.ArchTaskExecutor.isMainThread(ArchTaskExecutor.java:116)
at androidx.lifecycle.LiveData.assertMainThread(LiveData.java:461)
at androidx.lifecycle.LiveData.setValue(LiveData.java:304)
at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java:50)
at com.iainism.cardtap.card_exercise.CardExerciseViewModel.vmReset(CardExerciseViewModel.kt:47)
at com.iainism.cardtap.card_exercise.CardExerciseViewModel.<init>(CardExerciseViewModel.kt:42)
at com.iainism.cardtap.card_exercise.CardExerciseViewModelTest.<init>(CardExerciseViewModelTest.kt:48)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at org.junit.platform.commons.util.ReflectionUtils.newInstance(ReflectionUtils.java:453)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:62)
at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.invokeTestClassConstructor(ClassTestDescriptor.java:345)
at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.instantiateTestClass(ClassTestDescriptor.java:290)
at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.instantiateTestClass(ClassTestDescriptor.java:281)
at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.instantiateAndPostProcessTestInstance(ClassTestDescriptor.java:269)
at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.lambda$testInstancesProvider$2(ClassTestDescriptor.java:259)
at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.lambda$testInstancesProvider$3(ClassTestDescriptor.java:263)
我的单元测试开始的地方(包括堆栈跟踪中的行):
@ExtendWith(InstantTaskExecutorExtension::class)
class CardExerciseViewModelTest {
private val cardOne = Card("one", "test")
private val cardTwo = Card("two", "test")
private val cardThree = Card("three", "test")
private val testList = listOf(
cardOne,
cardTwo,
cardThree
)
private val testReverse = listOf(
cardThree,
cardTwo,
cardOne
)
private val testUnusedDeck = mockk<CardDeck> {
every { getCardFromTop() } returnsMany testList
every { getCardFromBottom() } returnsMany testReverse
every { putCardOnTop(cardOne) } just runs
every { putCardOnBottom(cardOne) } just runs
}
private val testUsedDeck = mockk<CardDeck> {
every { getCardFromTop() } returnsMany testList
every { getCardFromBottom() } returnsMany testReverse
every { putCardOnTop(cardOne) } just runs
every { putCardOnBottom(cardOne) } just runs
}
private val testViewModel = CardExerciseViewModel(testUnusedDeck, testUsedDeck)// <-----Line 48
@BeforeEach
fun init() {
clearAllMocks()
}
并InstantTaskExecutorExtension.kt
包含
class InstantTaskExecutorExtension : BeforeEachCallback, AfterEachCallback {
override fun beforeEach(context: ExtensionContext?) {
ArchTaskExecutor.getInstance()
.setDelegate(object : TaskExecutor() {
override fun executeOnDiskIO(runnable: Runnable) = runnable.run()
override fun postToMainThread(runnable: Runnable) = runnable.run()
override fun isMainThread(): Boolean = true
})
}
override fun afterEach(context: ExtensionContext?) {
ArchTaskExecutor.getInstance().setDelegate(null)
}
}
最后,ViewModel 的前几行,包括堆栈跟踪中突出显示的行是:
class CardExerciseViewModel(private val unusedCards: CardDeck, private val usedCards: CardDeck) : ViewModel() {
private val _selectedExercise = MutableLiveData<PossibleExercises>()
val selectedExercise: LiveData<PossibleExercises>
get() = _selectedExercise
// Card related stuff
private val _cardInView = MutableLiveData<Card>()
val cardInView: LiveData<Card>
get() = _cardInView
// ViewModel state stuff
private val _speechOn = MutableLiveData<Boolean>()
val speechOn: LiveData<Boolean>
get() = _speechOn
private val _paused = MutableLiveData<Boolean>()
val paused: LiveData<Boolean>
get() = _paused
private val _atBeginning = MutableLiveData<Boolean>()
val atBeginning: LiveData<Boolean>
get() = _atBeginning
private val _cardsDone = MutableLiveData<Boolean>()
val cardsDone: LiveData<Boolean>
get() = _cardsDone
init {
vmReset() // <-----Line 42
_selectedExercise.value = PossibleExercises.STANDARD_EXERCISES
}
fun vmReset() {
_paused.value = false // <-----Line 47
_cardsDone.value = false
_atBeginning.value = true
_speechOn.value = true
}
鉴于我已经进入androidx.arch.core.executor.TaskExecutor
,我不确定我哪里出错了?
解决方案
推荐阅读
- java - 找不到常见的超类 - Proguard
- intellij-idea - 在 IntelliJ IDEA 中的终端选项卡之间切换
- c# - 如何在 .NET Core 应用程序的 dll 被覆盖时停止关闭?
- angularjs - HTTP 状态 406 - 不可接受 Spring mvc
- mongodb - 这个 mongo 查询有什么问题?
- javascript - 如何在highcharts中获得相对于十字线的轴值
- raspbian - 在 Raspbian 上安装 GNU 收音机
- mysql - 如何解决实体框架打开 DataReader 问题
- java - where are the #list looped variables in Netbeans wizard freemarker
- node.js - AWS CodePipeline 将错误的 JobId 传递给 Lambda(nodejs)