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

这个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<MainActivity> activityScenarioRule
            = new ActivityScenarioRule<>(MainActivity.class);
ActivityScenario<NewMessageDetailActivity> 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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容