android - 在 init block 中使用 postValue 时出现 LiveData 单元测试错误

标签 android kotlin android-architecture-components android-livedata android-viewmodel

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

LoginViewModel.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 应该在使用实时数据时提供执行上下文,但是在 init block 中初始化实时数据时它不起作用。当省略 init block 时,它会按预期工作,但我需要初始化实时数据变量的可能性。

在对 View 模型进行单元测试时,有什么方法可以使实时数据初始化工作吗?

最佳答案

我设法使用提到的规则 - InstantTaskExecutorRule 对使用 LiveDataViewModel 进行了单元测试。但在我的例子中,规则 val 声明有点不同:

@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 block 也被调用得太早了。也许在 @Before 方法中创建它是合理的?喜欢:

private lateinit var viewModel: LoginViewModel

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

关于android - 在 init block 中使用 postValue 时出现 LiveData 单元测试错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52762418/

相关文章:

android - 如何在单个texview中将TextView更改为多尺寸

java - 将俄语文本保存为 pdf

java - 如何在 onCreate/主线程中获取房间列表的大小?

安卓。无法实例化 Worker

java - Kotlin:在具有加密主页的 Linux 上:java.io.FileNotFoundException(文件名太长)

android - 带有 Dagger 2 的 ViewModelProviders,无法掌握概念

Android HTTP 请求奇怪的 404 未找到问题

android - Cocos2d-x v3.7.1 可惜libcocos2dx已经停止

kotlin - Gradle的初学者问题

kotlin - 在异步调用完成之前按下后退按钮会发生什么?