在使用 AndroidX LiveData 和模拟视图模型进行检测测试时遇到问题



自从将项目移植到Android Jetpack库("AndroidX")以来,我无法获得涉及模拟视图模型和LiveData操作的插桩测试。

我构建了一个相对简单的应用程序,在这里表现出相同的错误:https://github.com/Spheniscine/courtcounter/tree/4e9d413f56ccdbded54d72abeba448f281af1a7f

应用程序本身工作正常,但 MainActivityTest 将失败并显示此错误:

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:8358)
at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:1364)
at android.view.ViewGroup.invalidateChild(ViewGroup.java:5433)
at android.view.View.invalidateInternal(View.java:13997)
at android.view.View.invalidate(View.java:13961)
at android.view.View.invalidate(View.java:13945)
at android.widget.TextView.checkForRelayout(TextView.java:8411)
at android.widget.TextView.setText(TextView.java:5011)
at android.widget.TextView.setText(TextView.java:4836)
at android.widget.TextView.setText(TextView.java:4811)
at com.github.spheniscine.udacityredone.courtcounter.ui.MainActivity$onCreate$$inlined$bindText$1.onChanged(LiveDataUtil.kt:45)
at androidx.lifecycle.LiveData.considerNotify(LiveData.java:113)
at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:131)
at androidx.lifecycle.LiveData.setValue(LiveData.java:289)
at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java:33)
at com.github.spheniscine.udacityredone.courtcounter.util.LiveDataUtilKt.setTo(LiveDataUtil.kt:26)
at com.github.spheniscine.udacityredone.courtcounter.MainActivityTest.scoreTeamA_changes_whenViewModelSet(MainActivityTest.kt:46)
at java.lang.reflect.Method.invoke(Native Method)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at androidx.test.internal.runner.junit4.statement.RunBefores.evaluate(RunBefores.java:80)
at org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:55)
at androidx.test.rule.ActivityTestRule$ActivityStatement.evaluate(ActivityTestRule.java:531)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at androidx.test.ext.junit.runners.AndroidJUnit4.run(AndroidJUnit4.java:104)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:56)
at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:388)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1960)

唯一对我有用的是使用 runOnUiThread 来执行涉及视图层次结构的代码行:

  @Test
  fun loadingIndicatorShownWhenLiveDataUpdated() {
    activityRule.activity.runOnUiThread {
      loadingLiveData.value = true
    }
    onView(withId(R.id.progressBar))
      .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
  }

如果您在 variable.value = true 上遇到问题,那是因为您没有在主线程上工作。为此,LiveData有一个特殊的方法。尝试使用:

variable.postValue(true)

根据文档:

setValue():

设置值。如果有活动观察者,则将值分派给他们。必须从主线程调用此方法。

postValue():

将任务发布到主线程以设置给定值。如果在主线程执行已发布任务之前多次调用此方法,则只会调度最后一个值。