Можно ли использовать IdlingResource от Espresso, чтобы дождаться появления определенного представления? - PullRequest
0 голосов
/ 31 мая 2018

В моем тесте у меня есть этап, когда после нажатия кнопки приложение выполняет много асинхронных вычислений и запросов к облачному сервису, после чего оно отображает определенный вид.

Можно ли использовать Espresso's IdlingResource Реализация ждать, пока не появится определенное представление?

Я прочитал ответы здесь , и комментарии, кажется, предлагают использовать вместо этого IdlingResource, но я не понимаюкак.Эспрессо, похоже, не имеет встроенного способа обработки длинных операций, но необходимость писать собственные циклы ожидания выглядит как хак.

Любой способ решить эту проблему, или я должен просто сделать это как ответ всвязанная ветка подсказывает?

Ответы [ 2 ]

0 голосов
/ 17 января 2019

Я черпал вдохновение у Анатолия, но вместо использования методов из View.class я все еще использую только ViewMatchers.

/**
 * {@link IdlingResource} that idles until a {@link View} condition is fulfilled.
 */
public class ViewIdlingResource implements IdlingResource {

    private final Matcher<View>    viewMatcher;
    private final Matcher<View>    idleMatcher;
    private       ResourceCallback resourceCallback;

    /**
     * Constructor.
     *
     * @param viewMatcher The matcher to find the view.
     * @param idlerMatcher The matcher condition to be fulfilled to be considered idle.
     */
    public ViewIdlingResource(final Matcher<View> viewMatcher, Matcher<View> idlerMatcher) {
        this.viewMatcher = viewMatcher;
        this.idleMatcher = idlerMatcher;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isIdleNow() {
        View view = getView(viewMatcher);
        boolean isIdle = idleMatcher.matches(view);

        if (isIdle && resourceCallback != null) {
            resourceCallback.onTransitionToIdle();
        }

        return isIdle;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
        this.resourceCallback = resourceCallback;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getName() {
        return this + viewMatcher.toString();
    }

    /**
     * Tries to find the view associated with the given {@link Matcher<View>}.
     */
    private static View getView(Matcher<View> viewMatcher) {
        try {
            ViewInteraction viewInteraction = onView(viewMatcher);
            Field finderField = viewInteraction.getClass().getDeclaredField("viewFinder");
            finderField.setAccessible(true);
            ViewFinder finder = (ViewFinder) finderField.get(viewInteraction);
            return finder.getView();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

А как использовать бездельник в вашем тестовом примере, я пропускаю ViewMatchers.isDisplayed () будет моим ожидаемым условием в idler.

private void waitUntilViewIsDisplayed(Matcher<View> matcher) {
        IdlingResource idlingResource = new ViewIdlingResource(matcher, isDisplayed());
        try {
            IdlingRegistry.getInstance().register(idlingResource);
            // First call to onView is to trigger the idler.
            onView(withId(0)).check(doesNotExist());
        } finally {
            IdlingRegistry.getInstance().unregister(idlingResource);
        }
    }

С этим вы можете передать любой Matcher.class в конструктор ViewIdlingResource, чтобы он стал обязательным условием для представления, найденного параметром viewMatcher.

0 голосов
/ 31 мая 2018

Ваш IdlingResource может выглядеть следующим образом:

import android.support.test.espresso.IdlingResource;
import android.support.test.espresso.ViewFinder;
import android.support.test.espresso.ViewInteraction;
import android.view.View;

import org.hamcrest.Matcher;

import java.lang.reflect.Field;

import static android.support.test.espresso.Espresso.onView;

public class ViewShownIdlingResource implements IdlingResource {

    private static final String TAG = ViewShownIdlingResource.class.getSimpleName();

    private final Matcher<View> viewMatcher;
    private ResourceCallback resourceCallback;

    public ViewShownIdlingResource(final Matcher<View> viewMatcher) {
        this.viewMatcher = viewMatcher;
    }

    @Override
    public boolean isIdleNow() {
        View view = getView(viewMatcher);
        boolean idle = view == null || view.isShown();

        if (idle && resourceCallback != null) {
            resourceCallback.onTransitionToIdle();
        }

        return idle;
    }

    @Override
    public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
        this.resourceCallback = resourceCallback;
    }

    @Override
    public String getName() {
        return this + viewMatcher.toString();
    }

    private static View getView(Matcher<View> viewMatcher) {
        try {
            ViewInteraction viewInteraction = onView(viewMatcher);
            Field finderField = viewInteraction.getClass().getDeclaredField("viewFinder");
            finderField.setAccessible(true);
            ViewFinder finder = (ViewFinder) finderField.get(viewInteraction);
            return finder.getView();
        } catch (Exception e) {
            return null;
        }
    }
}

Затем вы можете создать вспомогательный метод, ожидающий вашего представления:

public void waitViewShown(Matcher<View> matcher) {
    IdlingResource idlingResource = new ViewShownIdlingResource(matcher);///
    try {
        IdlingRegistry.getInstance().register(idlingResource);
        onView(matcher).check(matches(isDisplayed()));  
    } finally {
        IdlingRegistry.getInstance().unregister(idlingResource);
    }    
}

Наконец, в вашем тесте:

@Test
public void someTest() {
    waitViewShown(withId(R.id.<some>));

    //do whatever verification needed afterwards    
} 

Вы можете улучшить этот пример, заставив IdlingResource ожидать любое условие, а не только видимость.

...