Espresso 自用超时工具类

481 阅读2分钟

espresso 虽然有六十秒的超时等待,但是如果布局中没有匹配到的控件,它会立即报错(布局延迟或者是网络请求后才会加载布局它会报错)。

所以我引入了超时工具类:TimeoutEspresso

方法介绍:

工具类方法是跟Espresso 的方法是对应的,内部其实是一个超时循环,

onViewWithTimeout => onView

checkWithTimeout => check 内部实现同上

performWithTimeout=>performperformWithTimeput会循环多次操作。\color{red}{performWithTimeput 会循环多次操作。}

  • 查找和验证控件推荐使用Timeout 工具类.
  • 对view 进行操作,推荐Espresso 的原生onView()check()和perform() 内部实现同上

使用例子:

使用espresso 对一个字符是“确定”的控件进行验证点击:

使用TimeoutEspresso (默认十秒超时)点击:

注意方法要配套使用,不然循环时onViewWithTimeout设置超时无效)\color{red}{注意方法要配套使用,不然循环时onViewWithTimeout设置超时无效)}

performWithTimeput会循环多次操作,有可能会出现一些问题:\color{red}{performWithTimeput 会循环多次操作,有可能会出现一些问题:}

  • 查找和验证控件推荐使用Timeout 工具类.
  • 对view 进行操作,推荐Espresso 的原生onView()check()和perform()

使用TimeoutEspresso (自定义十五秒超时)点击:

操作控件容易频繁点击,推荐写法:

工具类:

import android.support.annotation.NonNull;
import android.support.test.espresso.Espresso;
import android.support.test.espresso.FailureHandler;
import android.support.test.espresso.NoMatchingViewException;
import android.support.test.espresso.Root;
import android.support.test.espresso.ViewAction;
import android.support.test.espresso.ViewAssertion;
import android.support.test.espresso.ViewInteraction;
import android.support.test.espresso.assertion.ViewAssertions;
import android.support.test.espresso.matcher.ViewMatchers;
import android.view.View;
import junit.framework.AssertionFailedError;
import org.hamcrest.Matcher;

/**
 * @author DrChen
 * @Date 2020/6/3. qq:1414355045 用以应对Espresso布局延时加载,View no matcher 的情况
 */

public class TimeoutEspresso {

    private static final int SLEEP_IN_A_LOOP_TIME = 50;

    private static final long DEFAULT_TIMEOUT_IN_MILLIS = 10 * 1000L;

    /**
     * Use instead of {@link Espresso#onView(Matcher)}
     *
     * @param timeoutInMillis timeout after which an error is thrown
     * @param viewMatcher view matcher to check for view
     * @return view interaction
     */
    public static TimedViewInteraction onViewWithTimeout(long timeoutInMillis,
            @NonNull final Matcher<View> viewMatcher) {
        final long startTime = System.currentTimeMillis();
        final long endTime = startTime + timeoutInMillis;
        do {
            try {
                ViewInteraction viewInteraction = Espresso.onView(viewMatcher);
//                viewInteraction.check(ViewAssertions.matches(ViewMatchers.isDisplayed()));
                return new TimedViewInteraction(viewInteraction);
            } catch (NoMatchingViewException ex) {
                SystemClock.sleep(1000);
                //ignore
            }
        }
        while (System.currentTimeMillis() < endTime);

        // timeout happens
        return new TimedViewInteraction(Espresso.onView(viewMatcher));//返回最后一次报错的内容
    }

    /**
     * Use instead of {@link Espresso#onView(Matcher)}. Same as {@link #onViewWithTimeout(long,
     * Matcher)} but with the default timeout {@link #DEFAULT_TIMEOUT_IN_MILLIS}.
     *
     * @param viewMatcher view matcher to check for view
     * @return view interaction
     */
    public static TimedViewInteraction onViewWithTimeout(@NonNull final Matcher<View> viewMatcher) {
        return onViewWithTimeout(DEFAULT_TIMEOUT_IN_MILLIS, viewMatcher);
    }

    /**
     * A wrapper around {@link ViewInteraction} which allows to set timeouts for view actions and
     * assertions.
     */
    public static class TimedViewInteraction {

        private ViewInteraction wrappedViewInteraction;

        public TimedViewInteraction(ViewInteraction wrappedViewInteraction) {
            this.wrappedViewInteraction = wrappedViewInteraction;
        }

        /**
         * @see ViewInteraction#perform(ViewAction...)
         */
        public TimedViewInteraction perform(final ViewAction... viewActions) {
            wrappedViewInteraction.perform(viewActions);
            return this;
        }

        /**
         * {@link ViewInteraction#perform(ViewAction...)} with a timeout of {@link
         * #DEFAULT_TIMEOUT_IN_MILLIS}.
         *
         * @see ViewInteraction#perform(ViewAction...)
         */
        public TimedViewInteraction performWithTimeout(final ViewAction... viewActions) {
            return performWithTimeout(DEFAULT_TIMEOUT_IN_MILLIS, viewActions);
        }

        /**
         * {@link ViewInteraction#perform(ViewAction...)} with a timeout.
         *
         * @see ViewInteraction#perform(ViewAction...)
         */
        public TimedViewInteraction performWithTimeout(long timeoutInMillis,
                final ViewAction... viewActions) {
            final long startTime = System.currentTimeMillis();
            final long endTime = startTime + timeoutInMillis;

            do {
                try {
                    return perform(viewActions);
                } catch (RuntimeException ex) {
                    //ignore
                }
                //view 的操作间隔1000 避免过快的重复点击
                SystemClock.sleep(1000);
            }
            while (System.currentTimeMillis() < endTime);

            return perform(viewActions);//返回最后一次报错的内容
        }

        /**
         * @see ViewInteraction#withFailureHandler(FailureHandler)
         */
        public TimedViewInteraction withFailureHandler(FailureHandler failureHandler) {
            wrappedViewInteraction.withFailureHandler(failureHandler);
            return this;
        }

        /**
         * @see ViewInteraction#inRoot(Matcher)
         */
        public TimedViewInteraction inRoot(Matcher<Root> rootMatcher) {
            wrappedViewInteraction.inRoot(rootMatcher);
            return this;
        }

        /**
         * @see ViewInteraction#check(ViewAssertion)
         */
        public TimedViewInteraction check(final ViewAssertion viewAssert) {
            wrappedViewInteraction.check(viewAssert);
            return this;
        }

        /**
         * {@link ViewInteraction#check(ViewAssertion)} with a timeout of {@link
         * #DEFAULT_TIMEOUT_IN_MILLIS}.
         *
         * @see ViewInteraction#check(ViewAssertion)
         */
        public TimedViewInteraction checkWithTimeout(final ViewAssertion viewAssert) {
            return checkWithTimeout(DEFAULT_TIMEOUT_IN_MILLIS, viewAssert);
        }

        /**
         * {@link ViewInteraction#check(ViewAssertion)} with a timeout.
         *
         * @see ViewInteraction#check(ViewAssertion)
         */
        public TimedViewInteraction checkWithTimeout(long timeoutInMillis,
                final ViewAssertion viewAssert) {
            final long startTime = System.currentTimeMillis();
            final long endTime = startTime + timeoutInMillis;

            do {
                try {
                    return check(viewAssert);
                } catch (RuntimeException ex) {
                    //ignore
                }catch (AssertionFailedError error){

                }

                SystemClock.sleep(SLEEP_IN_A_LOOP_TIME);
            }
            while (System.currentTimeMillis() < endTime);

            return check(viewAssert);//返回最后一次报错的内容
        }
    }
     }