Android Espresso(UI自动化测试)

设置测试环境

  1. 为了避免测试不稳定,我们强烈建议您在用于测试的虚拟或物理设备上关闭系统动画。在您的设备上,在设置 > 开发者选项下,停用以下三项设置:

    • 窗口动画缩放
    • 过渡动画缩放
    • Animator 时长缩放

Tips:部分手机可能无法正常运行会报找不到Intent错误,这种情况请更换测试机

  1. 添加依赖

        androidTestImplementation 'androidx.test:core:' + rootProject.coreVersion
        androidTestImplementation 'androidx.test.ext:junit:' + rootProject.extJUnitVersion
        androidTestImplementation 'androidx.test:runner:' + rootProject.runnerVersion
        androidTestImplementation 'androidx.test.espresso:espresso-core:' + rootProject.espressoVersion
        androidTestImplementation 'androidx.fragment:fragment-testing:' + rootProject.androidxFragmentVersion
        androidTestImplementation 'com.google.truth:truth:' + rootProject.truthVersion
        androidTestImplementation 'org.robolectric:annotations:' + rootProject.robolectricVersion
        androidTestImplementation 'androidx.test.espresso:espresso-intents:' + rootProject.espressoVersion
> 对应版本号 也可以自行更换别的版本号
    androidxFragmentVersion = "1.3.6"
    coreVersion = "1.4.1-alpha04"
    extJUnitVersion = "1.1.4-alpha04"
    runnerVersion = "1.5.0-alpha01"
    rulesVersion = "1.4.1-alpha04"
    espressoVersion = "3.5.0-alpha04"
    robolectricVersion = "4.6.1"
    truthVersion = "1.1.3"
  1. 将以下代码行添加到同一 build.gradle 文件的 android.defaultConfig 中:

    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
  1. 一般新建的项目会有两个文件夹存放测试代码的地方

image.png

要是想要将它们放在一个文件夹下面也可以配置

image.png

相关文档

  • 官方Demo github地址
  • Espresso谷歌文档中文版
  • Espresso文档
  • Espresso自定义匹配器以及各种常用Api释义
  • Espresso模拟返回相机或者相册结果
  • IdlingResource介绍
  • Espresso Demo合集

示例Demo

这个Demo有两个按钮,按钮(CHANGE TEXT)的作用就是把那个EditText里面的文本替换上面那个Hellow Espressol的文本

image.png

接下来运行第一个测试方法看看效果,顺带说一下,每个测试方法运行完成后会退出app的,这种情况不是闪退,先看测试通过的,测试代码的意思就是寻找一个id为editTextUserInput的控件,给它输入文案,然后点击CHANGE TEXT按钮替换文案,最后再看id 为textToBeChanged的TextView的文案是否跟替换的文案一致,一致就通过,不一致就失败

@Test
public void changeText_sameActivity() throws InterruptedException {
    // Type text and then press the button.
    onView(withId(R.id.editTextUserInput))
            .perform(typeText(STRING_TO_BE_TYPED), closeSoftKeyboard());
    onView(withId(R.id.changeTextBt)).perform(click());

    // Check that the text was changed.
    onView(withId(R.id.textToBeChanged)).check(matches(withText(STRING_TO_BE_TYPED)));
}
这个是测试通过的

这个是控制台对应的信息

咱们再看一下测试不通过的,不通过是因为我输入的是111111,它跟比对的值10086这个不相等,所以就测试不通过

@Test
public void changeText_sameActivity() throws InterruptedException {
    Thread.sleep(5000);
    // Type text and then press the button.
    onView(withId(R.id.editTextUserInput))
            .perform(typeText("111111"), closeSoftKeyboard());
    onView(withId(R.id.changeTextBt)).perform(click());

    // Check that the text was changed.
    onView(withId(R.id.textToBeChanged)).check(matches(withText(STRING_TO_BE_TYPED)));
}
测试失败的
控制台信息

常用Api介绍

UI测试的启动入口是根据这一行代码来启动的,启动方式也有区别,一种是加了Rule注解是属于rules库里面的启动方式,另一种是test.core库里面的

 @Rule public ActivityScenarioRule activityScenarioRule
            = new ActivityScenarioRule<>(MainActivity.class);
ActivityScenario scenario = ActivityScenario.launch(MainActivity.class);

使用onView或者onData查找视图的时候会出现找不到视图的情况,明明当前页面有这个id或者有这个文本的View,就是找不着,这是因为代码的执行速度比界面更快,所以代码执行的时候会出现View还没加载出来的情况,这样这个测试就失败了,关于这个问题,谷歌官方文档有解决办法

IdlingResource

1.停止指定的时间 不需要注入
2.根据真实耗时时间回调 需要注入测试代码到源码中

方法一 实现IdlingResource ,重写 isIdleNow(),这种方式相当于是给测试添加Thread.sleep
 class ElapsedTimeIdlingResource implements IdlingResource {
    private final long startTime;
    private final long waitingTime;
    private ResourceCallback resourceCallback;

    public ElapsedTimeIdlingResource(long waitingTime) {
        this.startTime = System.currentTimeMillis();
        this.waitingTime = waitingTime;
    }

    @Override
    public String getName() {
        return ElapsedTimeIdlingResource.class.getName() + ":" + waitingTime;
    }

    @Override
    public boolean isIdleNow() {
        long elapsed = System.currentTimeMillis() - startTime;
        boolean idle = (elapsed >= waitingTime);
        if (idle) {
            resourceCallback.onTransitionToIdle();
        }
        return idle;
    }

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

在需要后台耗时工作测试代码前面添加如下代码,也可在@Before注解的方法下添加,记得在@After取消注册,节省资源

idlingResource = new ElapsedTimeIdlingResource(1000 * 3);

Espresso.registerIdlingResources(idlingResource);
方法二注入待测类,这个方法需要改项目的源代码,在源代码添加测试代码,在耗时任务之前mIdlingResource.setIdleState(false);耗时任务结束后mIdlingResource.setIdleState(true);
public class SimpleIdlingResource implements IdlingResource {

    @Nullable
    private volatile ResourceCallback mCallback;

    // Idleness is controlled with this boolean.
    private AtomicBoolean mIsIdleNow = new AtomicBoolean(true);

    @Override
    public String getName() {
        return this.getClass().getName();
    }

    @Override
    public boolean isIdleNow() {
        return mIsIdleNow.get();
    }

    @Override
    public void registerIdleTransitionCallback(ResourceCallback callback) {
        mCallback = callback;
    }

    /**
     * Sets the new idle state, if isIdleNow is true, it pings the {@link ResourceCallback}.
     *
     * @param isIdleNow false if there are pending operations, true if idle.
     */
    public void setIdleState(boolean isIdleNow) {
        mIsIdleNow.set(isIdleNow);
        if (isIdleNow && mCallback != null) {
            mCallback.onTransitionToIdle();
        }
    }

在待测类中需要定义该方法

/**
 * Only called from test, creates and returns a new {@link SimpleIdlingResource}.
 */
@VisibleForTesting
@NonNull
public IdlingResource getIdlingResource() {
    if (mIdlingResource == null) {
        mIdlingResource = new SimpleIdlingResource();
    }
    return mIdlingResource;
}

Espresso 踩坑或一些不常用的方法示例

  1. onData()获取ListView或者GridView的item的方法,atPosition的索引是从0开始,我这是因为项目中的ListView封装了下拉刷新,刷新占在第一个位置,所以Item我这是从1开始
onData(allOf())
                .inAdapterView(withId(R.id.lv_content)) // listview的id
                .atPosition(1)                              // 所在位置
                .onChildView(withId(childViewId))  // item中子控件id
                .check(ViewAssertions.matches(isDisplayed()));
  1. 自动生成UI测试代码


    image.png

    运行以后在手机上执行对应操作就可以生成这样的一个界面,然后点个OK就会生成测试代码了,生成的测试代码点击运行后就会执行跟你刚刚的操作,不过项目复杂度高的话生成出的测试用例也会有失败的情况,一般生成的代码仅供参考


    image.png
  2. 导出测试结果


    image.png

你可能感兴趣的:(Android Espresso(UI自动化测试))