Я пытаюсь выполнить модульное тестирование ViewModel с помощью Livedata, и, несмотря на добавление расширения JUnit5 в мой тестовый класс для Instant Task Executor, я получаю:
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
, я не уверен, где я ошибся?