android - 使用 Espresso 的异步 UI 单元测试,IdlingResource 因竞争条件而失败

标签 android multithreading unit-testing asynchronous android-espresso

我正在尝试将我的一些 Robotium 单元测试转换为使用 Espresso,但在通过测试更新 UI 时遇到了问题。该测试针对的是一个 fragment ,该 fragment 是一种显示来自对象的数据的表单。该 fragment 有一个方法“BaseFragment.object_set(object)”,然后它将更新 UI 组件(有很多 TextView.setText(object.getField())) .

当我在测试中运行以下命令时...

BaseFragment fragment = (BaseFragment) getFragment(activity);
fragment.object_set(object);
assertNotNull(fragment.object_get());

...我得到这个异常...

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

当我改为运行它时...

activity.runOnUiThread(new Runnable() {
    public void run() {
        BaseFragment fragment = (BaseFragment) getFragment(activity);
        fragment.object_set(object);
    }
});
sleep(500);
assertNotNull(fragment.object_get());

...测试成功。如果没有 sleep(),它实际上会因竞争条件而失败。

因此,我的理解是 Espresso 的 sleep 是一种糟糕的形式,我们应该实现并注册一个 Idling 资源。我试过这个...

 sEspressoIdlingResource.setNotIdle();
 activity.runOnUiThread(new Runnable() {
     public void run() {
         BaseFragment fragment = (BaseFragment) getFragment(activity);
         fragment.object_set(object);
         sEspressoIdlingResource.setIdle();
     }
 });

...在调试时我可以看到 Espresso 实际上正在等待资源释放...只是这仍然会导致竞争条件,测试失败。看起来 UI 直到 AFTER activity.runOnUiThread() 完成后才更新,所以我想我只是不明白哪个线程是哪个的原则......有人可以帮忙吗并解释一下?

下面是一些重要的测试代码(下面的测试会由于竞争条件而失败)...

@RunWith(AndroidJUnit4.class)
public class MyTest{

    ActivityTestRule<AMain> mActivityTestRule;

    @Rule
    public ActivityTestRule<AMain> setUp() {
        // other code here
        mActivityTestRule = new ActivityTestRule<>(AMain.class);
        return mActivityTestRule;
    }

    @Before
    public void before() {
        Espresso.registerIdlingResources(BaseTest.sEspressoIdlingResource);
    }

    @After
    public void after() {
       Espresso.unregisterIdlingResources(BaseTest.sEspressoIdlingResource);
    }

    @Test
    public void test_1_showDetailsFragment() {
        // more code here for activity and object
        sEspressoIdlingResource.setNotIdle();
        activity.runOnUiThread(new Runnable() {
            public void run() {
                BaseFragment fragment = (BaseFragment) getFragment(activity);
                fragment.object_set(object);
                sEspressoIdlingResource.setIdle();
            }
        });
        assertNotNull(fragment.object_get());
        // more asserts here
    }
}

最佳答案

你现在可能已经自己回答了这个问题,但我相信我也有类似的问题,那就是 Espresso 没有等待 :

activity.runOnUiThread(new Runnable{...})

在执行其操作之前要完成的代码。 (就我而言):

onView(withId(R.id.where_city_search_input)).perform(typeText("Ch"));

据我所知,espresso 在执行其操作之前等待主线程处于空闲状态。所以如果你:

  1. 定义一个可运行的:

    Runnable myRunnable = new Runnable{...};
    
  2. 告诉 Instrumentation 等待这个 Runnable:

    InstrumentationRegistry.getInstrumentation().waitForIdle(myRunnable);
    

然后运行任何你想要的 Espresso 代码就可以了。最后它应该看起来像这样:

// Define Runnable:
Runnable myRunnable = new Runnable(){
    @Override
    public void run(){
        // What you want to run //
    }
};
// Run on UI Thread:
activity.runOnUiThread(myRunnable);
// Tell Instrumentation to wait:
InstrumentationRegistry.getInstrumentation().waitForIdle(myRunnable);

// BEGIN ESPRESSO CODE //
    ................
// END ESPRESSO CODE //

关于android - 使用 Espresso 的异步 UI 单元测试,IdlingResource 因竞争条件而失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34388098/

相关文章:

android - 更改屏幕方向后 TextView 内容丢失

android - 由于 Firebase 中 ValueEventListener 的匿名实现而导致内存泄漏

c++ - 如何使用线程运行类构造函数

perl - 测试 Web 应用程序的最佳方法是什么,尤其是在并发访问方面?

unit-testing - 为什么在使用 Jest 运行测试时,Trix 编辑器不会挂载到 Vue 组件中?

php - 返回意外消息的 JSON(或 PHP)

android - 从另一个 Activity 完成一个 Activity

C# 从线程访问类成员

multithreading - Qt中的线程ID

unit-testing - Selenium 批判