首页 > 解决方案 > 获取“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,我不确定我哪里出错了?

标签: kotlinandroid-livedatajunit5android-viewmodelmockk

解决方案


推荐阅读