Robolectric으로 ViewPager (및 CursorLoader) 테스트


91

누구든지 Robolectric을 사용하여 다음 설정을 테스트하는 방법을 알고 있습니까?

CursorLoader로로드 된 데이터 인 ViewPager를 포함하는 조각입니다.

아래 코드를 사용하면 CursorLoader가 뷰 페이저의 어댑터로 푸시되지 않습니다. 나는 await()전화에 갇혀있다 .

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
단위 테스트가 아닌 시스템 테스트를 더 많이 작성하려는 것 같습니다. 올바른 가정입니까? 그렇다면 Robotium 또는 Espresso로 훨씬 쉽게 할 수 있습니다. Robolectric은 Android 특정 참조가있는 단위 또는 통합 테스트를 작성하려고하지만 사용자에게 표시되는 내용을 테스트하지 않을 때 정말 유용합니다.
Elliott

약간의 말이 그립습니다. Robolectric은 사용자가보고있는 것을 테스트하는 데 도움을주고 있습니다. 내가 직면하게 될 문제는 프로덕션에서 올바른 코드 경로를 테스트 할 수없는 부트 스트래핑 코드를 너무 많이 작성해야한다는 것과 시간 제한 대기를 사용하려고하면 일반적으로 방법을 알 수 없기 때문에 테스트가 불안정해진다는 것입니다. 긴 비동기 작업이 걸립니다.
Elliott

빠른 테스트를 받으려고합니다. 저는 Robotium에서 테스트를 작성할 수 있고 자주 수행하지만 Robolectric보다 실행 속도가 훨씬 느립니다. 나에게 이상적인 세상은 2 세트의 테스트를 설정, 지원 및 유지하지 않는 것입니다. 이것이 제 의도였습니다.
Corey Scott

1
그냥 생각, Robolectric.runBackgroundTasks();좋은 - 아마 대신의await
웨스턴

1
나는 @Elliott에 동의합니다. 통합 테스트 또는 단위 테스트와 같은 시나리오 테스트를 만들려고 노력하고 있으며 robolectric을 해킹하고 악용하려는 경우 그 의미가 아니지만 가격이 있습니다. robolectric은 클래스가 아닌 스텁 만 제공합니다.
codeScriber 2015-06-28

답변:


1

확실하지는 않지만 내부 코드가 AsyncTask커서 로더의 loadInBackground()메서드 를 호출하기 위해 를 사용하려고합니다 . AsyncTask호출을 시도하기 때문에 교착 상태가 발생할 수 있습니다 onPostExecute(). 이 호출은 테스트 루틴의 스레드 인 기본 UI 스레드에서 실행을 시도합니다. 호출 스택에서 테스트 루틴이 대기 중이기 때문에 결코 일어날 수 없습니다.

테스트의 백그라운드에서 실제로 아무 일도 일어나지 않도록 조롱을 더 높은 수준으로 이동하십시오. 구글 `AsyncTask를 단위 테스트 안드로이드 교착 상태가 ' 다른 사람이 비슷한 문제로 실행 예를 볼 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.