Эспрессо-тест не пройден из-за привязки данных - PullRequest
1 голос
/ 21 октября 2019

Это мой класс представления модели:

class MainViewModel(
    private val schedulerProvider: BaseSchedulerProvider,
    private val api : StorytelService
) : BaseViewModel() {

    private val _posts = MutableLiveData<List<Post>>()
    val posts: LiveData<List<Post>>
        get() = _posts

    private val _status = MutableLiveData<Status>()
    val status: LiveData<Status>
        get() = _status

    init {
        showPhotos()
    }

    fun showPhotos() {
        EspressoIdlingResource.increment() // App is busy until further notice
        _status.postValue(Status.LOADING)
        compositeDisposable.add(api.getPhotos()
            .subscribeOn(schedulerProvider.io())
            .observeOn(schedulerProvider.ui())
            .doFinally {
                if (!EspressoIdlingResource.countingIdlingResource.isIdleNow) {
                    EspressoIdlingResource.decrement() // Set app as idle.
                }
            }
            .subscribe({
                _status.postValue(Status.SUCCESS)
                showPosts(it)
            }) {
                _status.postValue(Status.ERROR)
                Timber.e(it)
            })
    }

    private fun showPosts(networkPhotos: List<NetworkPhoto>) {
        EspressoIdlingResource.increment() // App is busy until further notice
        _status.postValue(Status.LOADING)
        compositeDisposable.add(api.getPosts()
            .subscribeOn(schedulerProvider.io())
            .observeOn(schedulerProvider.ui())
            .doFinally {
                if (!EspressoIdlingResource.countingIdlingResource.isIdleNow) {
                    EspressoIdlingResource.decrement() // Set app as idle.
                }
            }
            .subscribe({ networkPosts ->
                _status.postValue(Status.SUCCESS)
                _posts.postValue(
                    PostAndImages(networkPosts, networkPhotos).asDomaineModel()
                )
            }) {
                _status.postValue(Status.ERROR)
                Timber.e(it)
            })
    }

Это мой переработчикПросмотр макета:

<androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
            app:layout_behavior="@string/appbar_scrolling_view_behavior"
            app:showData="@{vm.status}"
            tools:listitem="@layout/post_item" />

А вот адаптер привязки:

@BindingAdapter("showData")
fun View.showData(status: Status) {
    visibility = if (status == Status.SUCCESS) View.VISIBLE else View.GONE
}

Каквы замечаете, что я использую EspressoIdlingResource, но когда я запускаю следующий тест эспрессо, он завершается неудачно:

    @Test
    fun shouldBeAbleToLoadList() {
        onView(withId(R.id.recycler_view)).check(matches(isDisplayed()))
    } 

Если я добавлю Thread.sleep (5000) в начале теста, он работает. Как это решить?

Ответы [ 2 ]

2 голосов
/ 21 октября 2019

Это может быть возможно с Idling Resource, но они немного утомительны.

Я только что обновил старый код viewMatcher:

/**
 * Perform action of waiting for a specific view id to be displayed.
 * @param viewId The id of the view to wait for.
 * @param millis The timeout of until when to wait for.
 */
public static ViewAction waitDisplayed(final int viewId, final long millis) {
    return new ViewAction() {
        @Override
        public Matcher<View> getConstraints() {
            return isRoot();
        }

        @Override
        public String getDescription() {
            return "wait for a specific view with id <" + viewId + "> has been displayed during " + millis + " millis.";
        }

        @Override
        public void perform(final UiController uiController, final View view) {
            uiController.loopMainThreadUntilIdle();
            final long startTime = System.currentTimeMillis();
            final long endTime = startTime + millis;
            final Matcher<View> matchId = withId(viewId);
            final Matcher<View> matchDisplayed = isDisplayed();

            do {
                for (View child : TreeIterables.breadthFirstViewTraversal(view)) {
                    if (matchId.matches(child) && matchDisplayed.matches(child)) {
                        return;
                    }
                }

                uiController.loopMainThreadForAtLeast(50);
            }
            while (System.currentTimeMillis() < endTime);

            // timeout happens
            throw new PerformException.Builder()
                    .withActionDescription(this.getDescription())
                    .withViewDescription(HumanReadables.describe(view))
                    .withCause(new TimeoutException())
                    .build();
        }
    };
}

, тогда вы должны сделать только:

@Test
    fun shouldBeAbleToLoadList() {
        onView(isRoot()).perform(waitDisplayed(R.id.recycler_view, 5000));
    } 

5000 - это тайм-аут 5 секунд (5000 миллис), вы можете изменить его, если хотите.

После выполнения waitDisplayed может случиться, что элементили время ожидания истекло. В последнем случае Exception будет брошено.

0 голосов
/ 21 октября 2019

Вам нужно будет создать Idling Resource для привязки. Вы можете проверить Пример компонентов архитектуры Android , которые имеют аналогичную реализацию. Вот что вам нужно искать:

  1. Во-первых, вам нужно добавить класс Idling Resource, который проверяет, есть ли ожидающие привязки (вы можете найти реализацию здесь )
  2. Теперь вы можете создать правило, которое будет автоматически регистрировать / отменять регистрацию Idling Resource для вас (вы можете найти реализацию здесь ).
  3. И теперь вы можетедобавьте это правило в ваш тест и проверьте, работает ли оно (пример реализации теста вы можете найти здесь ).
...