【问题标题】:Testing livedata with Robolectric使用 Robolectric 测试实时数据
【发布时间】:2019-06-30 14:54:22
【问题描述】:

我有以下测试检查活动是否通过视图模型正确地从存储库获取数据。

@Config(application = TestApplication::class)
@RunWith(RobolectricTestRunner::class)
@LooperMode(LooperMode.Mode.PAUSED)
class BusinessTests {
    private lateinit var viewModel: BusinessCollectionViewModel
    private lateinit var activity: BusinessCollectionVerticalActivity
    private lateinit var observer: Observer<Triple<NetworkState, PagedList<Edge<Business>>, TimeTracking?>>

    @Before
    fun setUp() {
        observer = mock()
    }

    @Test
    fun givenBusinessMock_whenVerticalCollection_thenBusinessVerticalWith2Items() {

        val activityScenario = ActivityScenario.launch(BusinessCollectionVerticalActivity::class.java)
        activityScenario.onActivity {
            activity = it
        }

        viewModel = ViewModelProviders.of(activity)[BusinessCollectionViewModel::class.java]

        viewModel.data.observeForever(observer)
        assert(viewModel.data.value?.second?.size == 2)
    }
}

问题是测试总是失败,但在调试中它正确通过,但是当我在断言中使用错误条件调试它时,会弹出以下异常。

java.lang.Exception: Main looper has queued unexecuted runnables. This might be the cause of the test failure. You might need a shadowOf(getMainLooper()).idle() call.

这真是奇怪的行为,我不知道该怎么办。当然,我尝试在观察之前添加 shadowOf(getMainLooper()).idle()。

我正在使用最新的 robolectric 4.3,这可能是一个错误吗?

【问题讨论】:

    标签: android robolectric android-livedata


    【解决方案1】:

    我遇到了和你一样的问题,我使用 instantTaskExecutorRule 解决了这个问题。

    您需要在您的应用程序 buil.gradle 文件中添加以下依赖项:

    testImplementation 'androidx.arch.core:core-testing:2.1.0'
    

    并在您的测试中包含以下规则:

    // Executes tasks in the Architecture Components in the same thread
    @get:Rule
    var instantTaskExecutorRule = InstantTaskExecutorRule()
    

    【讨论】:

    【解决方案2】:

    问题是运行 UI 测试的线程正在完成且没有任何错误,而 assert 正在另一个线程中运行。

    您可以更改测试方法以保持线程,直到它被运行 observeOnce 的线程释放:

    @Test
    fun givenBusinessMock_whenVerticalCollection_thenBusinessVerticalWith2Items() {
    
        val syncObject = Object()
    
        val activityScenario = ActivityScenario.launch(BusinessCollectionVerticalActivity::class.java)
        activityScenario.onActivity {
            activity = it
        }
    
        viewModel = ViewModelProviders.of(activity)[BusinessCollectionViewModel::class.java]
    
        viewModel.data.observeOnce {
            assert(it.second.size == 2)
    
            synchronized (syncObject) {
                syncObject.notify()
            }
        }
    
        synchronized (syncObject) {
            syncObject.wait()
        }
    }
    

    如果你想要更好的方法,你可以在你的数据中观察 Forever 并检查值:

    var observer: Observer<?> // replace ? by your type
    viewModel.data.observeForever(observer)
    assert(viewModel.data.value.second.size == 2)
    

    更多信息请参阅article

    【讨论】:

    • 第一个解决方案不起作用,第二个只是部分解决。当我运行测试时,它落在断言失败,因为 viewModel.data.value 为空。当我调试它时,viewModel.data.value.second.size 是 2,所以这是正确的,但是当我将断言条件更改为失败时,我在第一篇文章中提到的带有 main looper 的异常显示。你知道该怎么做吗?谢谢
    • 你在哪里用第二种方法放置assert?你能用代码更新问题吗?
    猜你喜欢
    • 2013-08-19
    • 2012-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多