首页 > 解决方案 > 在 init 块中使用 postValue 时的 LiveData 单元测试错误

问题描述

我正在尝试使用实时数据为视图模型编写单元测试。

登录视图模型.kt

class LoginViewModel @Inject constructor(
    val context: Context
): ViewModel() {
    val username = MutableLiveData<String>()
    val password = MutableLiveData<String>()
    val isLoginButtonEnabled = MediatorLiveData<Boolean>().apply {
        fun combineLatest(): Boolean {
            return !(username.value.isNullOrEmpty() || password.value.isNullOrEmpty())
        }
        addSource(username) { this.value = combineLatest() }
        addSource(password) { this.value = combineLatest() }
    }

    init {
        username.postValue("test")
        password.postValue("test")
    }
}

LoginViewModelTest.kt

@RunWith(MockitoJUnitRunner::class)
class LoginViewModelTest {
    @Rule
    @JvmField
    val instantTaskExecutorRole = InstantTaskExecutorRule()

    private val context = mock(Context::class.java)
    private val loginViewModel = LoginViewModel(context)

    @Test
    fun loginButtonDisabledOnEmptyUsername() {
        val observer = mock<Observer<Boolean>>()
        loginViewModel.isLoginButtonEnabled.observeForever(observer)
        loginViewModel.username.postValue("")

        verify(observer).onChanged(false)
    }
}

我的单元测试在该行抛出以下异常username.postValue("test")

java.lang.RuntimeException: Method getMainLooper in android.os.Looper not mocked. See http://g.co/androidstudio/not-mocked for details.

应该在InstantTaskExecutorRule使用实时数据时提供执行上下文,但是在初始化 -block 中的实时数据时它不起作用init。省略init-block 时,它可以按需要工作,但我需要初始化实时数据变量的可能性。

在单元测试视图模型时,有什么方法可以使实时数据初始化工作?

标签: androidkotlinandroid-architecture-componentsandroid-livedataandroid-viewmodel

解决方案


我设法使用提到的 rula -对我ViewModel的使用进行了单元测试。但在我的情况下,规则 val 声明有点不同:LiveDataInstantTaskExecutorRule

@Suppress("unused")
@get:Rule
val instantTaskExecutorRule: InstantTaskExecutorRule = InstantTaskExecutorRule()

编辑:

@Before
@Throws(Exception::class)
fun prepare() {
    MockitoAnnotations.initMocks(this)
}

编辑2:

由于某些奇怪的原因,我无法重现这一点:) 另外,我认为问题可能是因为您初始化 ViewModel 的方式 -

private val loginViewModel = LoginViewModel(context)

我认为它初始化得太早了,因此它的 init 块也被调用得太早了。也许在方法中创建它是合理的@Before?喜欢:

private lateinit var viewModel: LoginViewModel

@Before
@Throws(Exception::class)
fun prepare() {
    loginViewModel = LoginViewModel(context)
}

推荐阅读