【问题标题】:How to test a ListActivity with test data?如何使用测试数据测试 ListActivity?
【发布时间】:2015-08-10 10:17:03
【问题描述】:

我正在为一个简单的 Android 应用程序(这是一个学校项目)编写测试,但在测试扩展 Android 的 ListActivity 的 ContactListActivity 活动时遇到问题。

我想测试什么

单击 ContactListActivity 的 ListView 中的第一项并检查 ContactDetailActivity 是否已启动。

问题

列表数据来自 SQLite 数据库。为了测试,我将测试数据加载到 ListView 中,因此测试不会使用实时数据。加载测试数据工作正常。在运行测试时观察模拟器,我可以看到正在启动的活动以及出现在列表中的测试数据。但是,尝试访问第一个(也是唯一一个)列表项失败。

测试方法

@UiThreadTest
public final void testLoadContactDetail() {
    ListView list = activity.getListView();
    assertNotNull(list);

    ContactsListAdapter adapter = new ContactsListAdapter(
        getInstrumentation().getContext(),
        createData() // Creates an ArrayList of test data
    );
    list.setAdapter(adapter);
    adapter.notifyDataSetChanged();

    // list.getAdapter().getCount() is expectedly 1
    // list.getChildCount() is unexpectedly 0

    assertNotNull(list.getChildAt(0)); // Assertion fails

    // (...)
}

可以看出,我使用@UIThreadTest 对测试进行注释以便能够操作视图元素。使用测试数据创建一个新的 ListAdapter 并将其设置为列表。然后,adapter.notifyDataSetChanged() 确保列表知道新数据。

问题

如何将 ActivityInstrumentationTestCase2 中的测试数据加载到 ListView 中,这样数据不仅会显示在屏幕上,而且实际上“在那里”,这意味着可以使用 list.getChildAt(0) 获取列表项并单击?

整个测试用例

public class ContactListActivityFunctionalTest extends
    ActivityInstrumentationTestCase2<ContactListActivity> {

    private ContactListActivity activity;

    public ContactListActivityFunctionalTest() {
        super(ContactListActivity.class);
    }

    protected void setUp() throws Exception {
        super.setUp();
        setActivityInitialTouchMode(false);
        activity = getActivity();
    }

    protected void tearDown() throws Exception {
        super.tearDown();
    }

    @UiThreadTest
    public final void testLoadContactDetail() {
        ListView list = activity.getListView();
        assertNotNull(list);

        ContactsListAdapter adapter = new ContactsListAdapter(
            getInstrumentation().getContext(),
            createData()
        );
        list.setAdapter(adapter);
        adapter.notifyDataSetChanged();

        assertNotNull(list.getChildAt(0));

        // Anything beyond this point is never executed,
        // because the above assertion fails, and I have no idea
        // if this test code is correct at all.

        ActivityMonitor monitor = getInstrumentation().addMonitor(
            ContactDetailActivity.class.getName(), null, false
        );

        TouchUtils.clickView(this, list.getChildAt(0));

        ContactDetailActivity contactDetailActivity = 
            (ContactDetailActivity)monitor.waitForActivityWithTimeout(2000);
        assertNotNull(contactDetailActivity);
        assertTrue(getInstrumentation().checkMonitorHit(monitor, 1));
        contactDetailActivity.finish();
    }

    private List<ContactInterface> createData() {
        ContactInterface contact = new Contact();
        contact.setId(1L);
        contact.setName("Unit Test").setPhone("0123456789").setPosition(3);
        List<ContactInterface> contacts = new ArrayList<ContactInterface>();
        contacts.add(contact);
        return contacts;
    }
}

【问题讨论】:

    标签: android android-testing


    【解决方案1】:

    看起来listView.getChildAt 方法返回了可见视图。 https://stackoverflow.com/a/6767006/693752

    所以,我的猜测是该项目尚不可见。没有,因为 getChildCount 返回 0。也许你应该:

    • 在断言之前稍等片刻。好的,它很脏,但 UI 测试有时需要它。
    • 将断言发布到 ui 线程上的可运行对象中,以便在执行列表视图后执行。这将使您的测试变得更加复杂,因为您必须将未来的可运行线程和当前的测试线程同步为 countDownLatch。为此,您应该考虑不使用@UIThreadTest。

    【讨论】:

    • 非常感谢您的意见。在断言之前我尝试等待几秒钟,不幸的是无济于事。我终于屈服并得到了自己的“Android 应用程序测试指南”。按照其中的配方,我编写了一个基于ActivityUnitTestCase&lt;T&gt; 的新测试用例,效果很好。
    • 我以前从未听说过这门课。非常感谢。
    【解决方案2】:

    我知道我已经询问过如何从 ActivityInstrumentationTestCase2 中加载测试数据,但在这种特殊情况下,问题的答案可能是使用 ActivityUnitTestCase 而不是 ActivityInstrumentationTestCase2:

    1. 正在测试一般活动行为,而不是与其他组件的交互
    2. 嗯,它有效...

    这是重写的工作测试用例,用于测试 ListView 是否存在以及在单击列表的第一项后是否启动了正确的活动。

    public class ContactListActivityTest
        extends ActivityUnitTestCase<ContactListActivity> {
    
        private ContactListActivity activity;
    
        public ContactListActivityTest() {
            super(ContactListActivity.class);
        }
    
        protected void setUp() throws Exception {
            super.setUp();
            Intent intent = new Intent(
                getInstrumentation().getTargetContext(), ContactListActivity.class
            );
            startActivity(intent, null, null);
            activity = getActivity();
        }
    
        protected void tearDown() throws Exception {
            super.tearDown();
        }
    
        public final void testItemClick() {
            getInstrumentation().callActivityOnStart(activity);
            getInstrumentation().callActivityOnResume(activity);
    
            // Check if list exists
            ListView list = activity.getListView();
            assertNotNull("Intent was null", list);
    
            // Load test data
            ContactsListAdapter adapter = new ContactsListAdapter(
                getInstrumentation().getContext(),
                createData()
            );
            list.setAdapter(adapter);
            adapter.notifyDataSetChanged();
    
            assertEquals(2, adapter.getCount());
    
            // Check if list has at least one item to click
            View firstItem = list.getAdapter().getView(0, null, null);
            assertNotNull(firstItem);
    
            // Perform a click on the first item
            list.performItemClick(
                firstItem,
                0,
                list.getAdapter().getItemId(0)
            );
    
            // Check if the contact details activity got started
            Intent intent = getStartedActivityIntent();
            assertNotNull(intent);
            assertEquals(
                ContactDetailActivity.class.getName(),
                intent.getComponent().getClassName()
            );
        }
    
        private List<ContactInterface> createData() {
            List<ContactInterface> contacts = new ArrayList<ContactInterface>();
    
            ContactInterface contact = new Contact();
            contact.setId(1L);
            contact.setName("Jane Doe").setPhone("0123456789").setPosition(1);
            contacts.add(contact);
    
            ContactInterface contact2 = new Contact();
            contact2.setId(2L);
            contact2.setName("John Doe").setPhone("0234567890").setPosition(2);
            contacts.add(contact2);
    
            return contacts;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-04-27
      • 2015-01-26
      • 2012-04-24
      • 1970-01-01
      • 2020-09-22
      • 2016-03-13
      • 1970-01-01
      相关资源
      最近更新 更多