Thử nghiệm ViewPager (và CursorLoader) bằng Robolectric


91

Có ai biết cách kiểm tra thiết lập sau bằng Robolectric không?

Phân đoạn chứa ViewPager, dữ liệu được tải bằng CursorLoader.

Với đoạn mã dưới đây, CursorLoader không bao giờ được đẩy vào bộ điều hợp cho máy xem trang. Tôi bị kẹt trong await()cuộc gọi.

EventsFragmentTest.java:

@RunWith(CustomRobolectricTestRunner.class)
public class EventsFragmentTest extends AbstractDbAndUiDriver
{
    // which element in the view pager we are testing
    private static final int           TEST_INDEX = 0;

    protected SherlockFragmentActivity mActivity;
    protected EventsFragment_          mFragment;

    @Override
    @Before
    public void setUp() throws Exception
    {
        // create activity to hold the fragment
        this.mActivity = CustomRobolectricTestRunner.getActivity();

        // create and start the fragment
        this.mFragment = new EventsFragment_();
    }

    @Test
    public void sanityTest()
    {
        // create an event
        final Event event = this.createEvent();

        // create mock cursor loader
        final Cursor cursor = this.createMockEventCursor(event);
        this.mFragment.setCursorLoader(mock(CursorLoader.class));
        when(this.mFragment.getCursorLoader().loadInBackground()).thenReturn(cursor);
        CustomRobolectricTestRunner.startFragment(this.mActivity, this.mFragment);

        await().atMost(5, SECONDS).until(this.isCursorLoaderLoaded(), equalTo(true));

        // check for data displayed
        final TextView title = this.getTextView(R.id.event_view_title);
        final TextView text = this.getTextView(R.id.event_view_text);

        // exists and visible is enough for now
        this.getImageView(R.id.event_view_image);

        assertThat(title.getText().toString(), equalTo(event.getTitle()));
        assertThat(text.getText().toString(), is(event.getText()));

        // clean up
        cursor.close();
    }

    @Override
    protected View getRootView()
    {
        return ((ViewPager) this.mFragment.getView().findViewById(R.id.events_pager)).getChildAt(TEST_INDEX);
    }

    private Callable<Boolean> isCursorLoaderLoaded()
    {
        return new Callable<Boolean>()
        {
            public Boolean call() throws Exception
            {
                return EventsFragmentTest.this.mFragment.isCursorLoaderLoaded(); // The condition that must be fulfilled
            }
        };
    }

    /**
     * Create an event
     * 
     * @return
     */
    protected Event createEvent()
    {
        // create a random event
        final Event event = new Event();
        event.setImage(null);
        event.setLink("/some/link/" + RandomUtils.getRandomString(5)); //$NON-NLS-1$
        event.setResourceUri("/rest/uri/" + RandomUtils.getRandomDouble()); //$NON-NLS-1$
        event.setText("this is a test object " + RandomUtils.getRandomString(5)); //$NON-NLS-1$
        return event;
    }

    protected Cursor createMockEventCursor(final Event event)
    {
        // Create a mock cursor.
        final Cursor cursor = new CursorWrapper(mock(MockCursor.class));

        when(cursor.getCount()).thenReturn(1);
        when(cursor.getString(cursor.getColumnIndexOrThrow(EventTable.COLUMN_TEXT))).thenReturn(event.getText());
        when(cursor.getString(cursor.getColumnIndexOrThrow(EventTable.COLUMN_TITLE))).thenReturn(event.getTitle());
        when(cursor.getString(cursor.getColumnIndexOrThrow(EventTable.COLUMN_IMAGE))).thenReturn(event.getImage());
        when(cursor.getString(cursor.getColumnIndexOrThrow(EventTable.COLUMN_LINK))).thenReturn(event.getLink());
        when(cursor.getString(cursor.getColumnIndexOrThrow(EventTable.COLUMN_RESOURCE_URI))).thenReturn(
                        event.getResourceUri());

        // return created event
        return cursor;
    }

}

EventsFragment.java

@EFragment(resName = "events_fragment")
public class EventsFragment extends SherlockFragment implements LoaderCallbacks<Cursor>
{
    @ViewById(R.id.events_pager)
    protected ViewPager             mPager;

    @ViewById(R.id.events_indicator)
    protected CirclePageIndicator   mIndicator;

    @Pref
    protected ISharedPrefs_         mPreferences;

    protected EventsFragmentAdapter pageAdapter;

    private CursorLoader            mCursorLoader;

    /**
     * initialise the cursoradapter and the cursor loader manager.
     */
    @AfterViews
    void init()
    {
        final SherlockFragmentActivity activity = this.getSherlockActivity();

        this.pageAdapter = new EventsFragmentAdapter(activity.getSupportFragmentManager(), null);
        this.mPager.setAdapter(this.pageAdapter);
        this.mIndicator.setViewPager(this.mPager);
        this.getLoaderManager().initLoader(this.mPager.getId(), null, this);
    }

    /* (non-Javadoc)
     * @see android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader(int, android.os.Bundle)
     */
    @Override
    public Loader<Cursor> onCreateLoader(final int arg0, final Bundle arg1)
    {
        if (this.mCursorLoader == null)
        {
            // set sort to newest first
            final String sortOrder = BaseColumns._ID + " DESC"; //$NON-NLS-1$

            this.mCursorLoader = new CursorLoader(this.getActivity(), EventContentProvider.CONTENT_URI,
                            EventTable.getProjection(), AbstractDbTable.getWhereCondition(null),
                            AbstractDbTable.getWhereArgs(this.mPreferences, null), sortOrder);
        }
        return this.mCursorLoader;
    }

    /* (non-Javadoc)
     * @see android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished(android.support.v4.content.Loader, java.lang.Object)
     */
    @Override
    public void onLoadFinished(final Loader<Cursor> arg0, final Cursor cursor)
    {
        this.pageAdapter.swapCursor(cursor);

    }

    /* (non-Javadoc)
     * @see android.support.v4.app.LoaderManager.LoaderCallbacks#onLoaderReset(android.support.v4.content.Loader)
     */
    @Override
    public void onLoaderReset(final Loader<Cursor> arg0)
    {
        this.pageAdapter.swapCursor(null);
    }

    /**
     * Required for testing only.
     * 
     * @param cursorLoader
     */
    public void setCursorLoader(final CursorLoader cursorLoader)
    {
        this.mCursorLoader = cursorLoader;
    }

    /**
     * Required for testing only.
     * 
     * @param cursorLoader
     */
    public CursorLoader getCursorLoader()
    {
        return this.mCursorLoader;
    }

    public boolean isCursorLoaderLoaded()
    {
        return (this.pageAdapter.getCursor() != null);
    }
}

6
Có vẻ như bạn đang cố gắng viết nhiều Bài kiểm tra hệ thống hơn là bài kiểm tra Đơn vị, đó có phải là một giả định đúng không? Nếu vậy, điều này có thể được thực hiện dễ dàng hơn nhiều với Robotium hoặc Espresso. Robolectric thực sự hữu ích khi bạn đang cố gắng viết các bài kiểm tra đơn vị hoặc tích hợp có các tham chiếu cụ thể của Android nhưng không kiểm tra những gì người dùng sẽ thấy.
Elliott

Tôi hơi lỡ nói, Robolectric đang cố gắng giúp bạn kiểm tra những gì người dùng đang nhìn thấy. Vấn đề mà tôi nghĩ bạn sẽ gặp phải là bạn đang phải viết quá nhiều mã đóng đai khởi động, điều này sẽ không thể kiểm tra đường dẫn mã chính xác trong quá trình sản xuất và việc cố gắng sử dụng thời gian chờ thường làm cho quá trình kiểm tra trở nên giòn giã vì không biết cách các tác vụ không đồng bộ sẽ thực hiện.
Elliott

Tôi đang cố gắng làm các bài kiểm tra nhanh. Tôi có thể và thường làm các bài kiểm tra viết trong Robotium nhưng chúng chạy chậm hơn đáng kể so với Robolectric. Một thế giới lý tưởng đối với tôi sẽ không có thiết lập, hỗ trợ và duy trì 2 bộ kiểm tra. Đây là ý định của tôi ở đây.
Corey Scott

1
Chỉ là một suy nghĩ, Robolectric.runBackgroundTasks();bất kỳ điều gì tốt - có thể thay vìawait
weston

1
tôi đồng ý với @Elliott rằng bạn đang cố gắng tạo các bài kiểm tra tích hợp hoặc bài kiểm tra kịch bản như bài kiểm tra đơn vị và nó không phải là ý nghĩa của nó, nếu bạn muốn hack \ lạm dụng tiền phạt robot, nhưng phải trả giá. robolectric chỉ cung cấp sơ khai, không cung cấp các lớp.
codeScriber

Câu trả lời:


1

Tôi không chắc chắn, nhưng tôi cá rằng mã nội bộ đang cố gắng sử dụng một AsyncTaskđể gọi loadInBackground()phương thức của bộ tải con trỏ . Bạn có thể thấy bế tắc vì AsyncTaskcố gắng gọi raonPostExecute() . Cuộc gọi đó sẽ cố gắng chạy trong chuỗi giao diện người dùng chính của bạn, đây sẽ là chuỗi của quy trình kiểm tra của bạn. Điều đó không bao giờ có thể xảy ra vì bạn đang bị mắc kẹt trong quá trình chờ đợi với thói quen kiểm tra của mình trên ngăn xếp cuộc gọi.

Hãy thử chuyển chế độ chế nhạo của bạn lên một cấp độ cao hơn để không có gì thực sự xảy ra trong nền từ bài kiểm tra. google `` AsyncTask unit test android deadlock '' để xem các ví dụ trong đó những người khác gặp sự cố tương tự.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.