Android自动化测试--Espresso

小组2018的工作重心可能会放在app性能优化这一块。千分之八的崩溃率被大佬们屌了无数次。先定一个千分之三的小目标。所以接下来的工作要围绕稳定性和流畅性来展开。那么无可避免的会经常使用到android自动化测试了。

下面正式开始

首先第一步要做的就是集成Espresso测试环境,非常简单,在你要测试的Module的gradle里添加如下依赖:

androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    }
androidTestCompile 'com.android.support.test:rules:1.0.1'

举个栗子:

@RunWith(AndroidJUnit4.class)
@LargeTest
public class MainActivityInstrumentationTest {

    @Rule
    public ActivityTestRule mActivityRule = new ActivityTestRule<>(
            MainActivity.class);

    @Test
    public void sayHello(){
        onView(withText("Say hello!")).perform(click());

        onView(withId(R.id.textView)).check(matches(withText("Hello, World!")));
    }
}

上面这段代码是android官网上面的,关于Espresso的原理我就不说了,我也讲不来,强行讲的话,也讲的太肤浅.想了解原理的可以去Google一把.
那么我们首先关注两个注解:

@Rule:

顾名思义,测试规则,官方的解释是你可以在该注解下引用一个规则或定义一个方法,而你引用或定义的就是测试规则,这样说可能不是很清楚,可以看该注解下面的一句话:

public ActivityTestRule mActivityRule = new ActivityTestRule<>(
            MainActivity.class);

这句话就定义了一个测试规则,可以看到构造方法的参数里指定了一个 MainActivity.class,具体的体现就是当你运行这段测试代码时,app将会直接打开 MainActivity界面然后进行你所定义的测试用例.所以当你想直接测试某个界面时,你可以把那个界面填到这个参数里,这样就直接打开你指定的界面进行测试了.

@Test:

该注解用来定义一个测试用例,当你的测试类运行时,所执行的代码就是Test注解下的(Espresso还提供了其他的一些注解,比如:@After,@Before等,具体的用法可以去我上面写的android官网上查看),当然上面那段代码对应的就是sayHello测试方法,sayHello方法里所定义的就是要测试的内容,该内容的含义为:

onView(withText("Say hello!")).perform(click());

含义:根据文本”Say hello!”找个这个控件然后执行该控件的点击方法.

onView(withId(R.id.textView)).check(matches(withText("Hello, World!")));

含义:根据id “textView”找到这个控件然后检查该控件上面显示的文本是不是”Hello, World!”.
是不是很容易理解?非常语义化,一般都能看的懂.Espresso还提供了其他的方法供我们测试时调用,下面会列举一些常用的.

但是在列举常用的方法之前,需要先说明几点Espresso的注意事项,不然当你测试的时候会因为Espresso报的各种错误气个半死!!!

  • 无论是通过withId()找控件还是通过withText()找控件或者其他方式比如withClassName(),withResourceName(),withTagKey()等方法,都要一定保证你所找的控件在当前页面确实存在且可见,不然会报:NoMatchingViewException,当然你可能还会碰到其他异常,比如AmbiguousViewMatcherException,AppNotIdleException异常等等,具体的报错原因可以到android官网查看,地址如下:Espresso的各种异常
  • 如果你要测试AdapterView ,比如 ListView 或GridView等,使用上面的onView()方法是无效的,因为AdapterView的布局item是动态呈现的,没法直接指定,所以当你要测试AdapterView时,请把onView()方法换成onData() 方法,与onView()方法返回ViewInteraction类似,onData()方法返回DataInteraction,二者用法基本都是一样的.

perform()参数中常用的方法:

在上面的一段官网代码中,我们用到了perform(click()),那么除了click()方法还有其他功能强大的方法可以供我们使用,下面列举一些常用的方法:

  • click():
    返回一个点击action,Espresso利用这个方法执行一次点击操作,就和我们自己手动点击按钮一样,只不过Espresso把点击这个操作自动化了,下面的方法都是一样的道理,就不再赘述了.
  • clearText():
    返回一个清除指定view中的文本action,在测试EditText时用的比较多
  • swipeLeft():
    返回一个从右往左滑动的action,这个在测试ViewPager时特别有用
  • swipeRight():
    返回一个从左往右滑动的action,这个在测试ViewPager时特别有用
  • swipeDown():
    返回一个从上往下滑动的action
  • swipeUp():
    返回一个从下往上滑动的action
  • closeSoftKeyboard():
    返回一个关闭输入键盘的action
  • pressBack():
    返回一个点击手机上返回键的action
  • doubleClick():
    返回一个双击action
  • longClick():
    返回一个长按action
  • scrollTo():
    返回一个移动action
  • replaceText():
    返回一个替换文本action
  • openLinkWithText():
    返回一个打开指定链接action

除了以上的常用方法还有其他一些不常用的,想继续研究的可以查看Espresso中的ViewActions类,需要注意的是,所有的方法包括上面说到的和没说到的,都有一个必须的前提条件,就是你要执行的view必须在当前界面上显示出来,这有两层意思:
1,当前界面必须能找到这个控件
2,这个控件必须是可见的
这是以上所有方法中通用的要求,当然有些方法还有额外的要求,比如必须要先获取焦点等

比如我们现在有这样一个场景:打开软件,滑动Viewpager的3个页面,在最后一个页面点击开始体验按钮进入主界面,点击预约叫车,由于没有登录所以会跳转到登陆页面,输入手机号,然后点击获取验证码按钮,然后输入验证码,最后点击登陆.
整个流程对应的代码测试代码如下:

@LargeTest
@RunWith(AndroidJUnit4.class)
public class StartActivityTest {

    @Rule
    public ActivityTestRule<StartActivity> mActivityTestRule = new ActivityTestRule<>(StartActivity.class);

    @Test
    public void startActivityTest() {
        //根据id找到ViewPager页面,并判断是否可见
        ViewInteraction appCompatViewPager = onView(
                allOf(withId(R.id.viewPager), isDisplayed()));
        // 向左滑动viewpager页面,下面3句也可以写成一句话,Espresso会从左到右依次执行
        // appCompatViewPager.perform(swipeLeft(),swipeLeft(),swipeLeft());
        appCompatViewPager.perform(swipeLeft());
        appCompatViewPager.perform(swipeLeft());
        appCompatViewPager.perform(swipeLeft());
        //根据文本找到"开始体验"按钮,并判断是否可见
        ViewInteraction appCompatButton = onView(
                allOf(withText("开始体验"), isDisplayed()));
        //执行按钮的点击操作
        appCompatButton.perform(click());
        //根据控件的id和该控件的父布局id找到控件,并判断是否可见
        ViewInteraction appCompatImageView = onView(
                allOf(withId(R.id.appointmentCallCar), withParent(withId(R.id.callCarLayout)), isDisplayed()));
        //执行该控件的点击操作
        appCompatImageView.perform(click());
        //根据id找到控件,并判断是否可见
        ViewInteraction appCompatEditText = onView(
                allOf(withId(R.id.phoneNumber), isDisplayed()));
        //执行替换文本操作,说白了就是输入文本,输入完毕之后关闭输入法键盘
        appCompatEditText.perform(replaceText("18894001263"), closeSoftKeyboard());
        //根据id和显示的文本内容找到控件,并判断是否可见
        ViewInteraction appCompatButton2 = onView(
                allOf(withId(R.id.getPassword), withText("获取验证码"), isDisplayed()));
        //执行该控件的点击操作
        appCompatButton2.perform(click());
        //根据id找到控件,并判断是否可见
        ViewInteraction appCompatEditText2 = onView(
                allOf(withId(R.id.password), isDisplayed()));
        //执行替换文本操作,说白了就是输入文本,输入完毕之后关闭输入法键盘
        appCompatEditText2.perform(replaceText("2454"), closeSoftKeyboard());
        //根据id和显示的文本内容找到控件,并判断是否可见
        ViewInteraction appCompatButton3 = onView(
                allOf(withId(R.id.login), withText("登录"), isDisplayed()));
        //执行该控件的点击操作
        appCompatButton3.perform(click());
    }
}

这里还需要说明一点,当所有的测试用例执行完毕之后,Espresso会自动关闭界面,根据动图也可以看到,当点击完登陆按钮之后,又回到了系统屏幕界面.

当测试完毕之后,在Android Studio的”Run”控制台可以看到测试结果,如下图:
如果测试顺利通过,会在下图中左侧显示All Test Passed:


测试通过截图

如果测试没有通过,则会在右侧的控制台中输入错误信息,我们可以根据这些错误信息修改我们的代码然后再次进行测试.

以上的测试代码只是测试了启动和登陆功能,可以看到,套路都是一样的:根据id或文本等条件找到控件,然后执行控件的相关操作,这些代码都是重复的,唯一变化的就是执行的操作和查找的条件不一样,我们试想一下,如果项目一旦很大,我们一个一个手动的编写测试代码是不是很麻烦,并且都是一样的套路,没有一点技术含量.如果有种方式能自动生成这些测试代码,而我们只需要根据具体的测试情况修改甚至不改这些自动生成的代码就能完成测试,是不是就可以极大的节省我们的时间?而恰好Android Studio2.2版本提供了一个使用Espresso框架进行测试的图形化界面–Record Espresso Test功能,通过这个功能,我们只需要把软件运行到真机或模拟器上,然后就可以像平常手动测试软件一样,按照业务逻辑点击/滑动即可,Record Espresso Test功能会自动生成相应的测试代码,我们运行生成的测试代码,Espresso就可以自动的按照我们刚才操作的顺序自动的完成测试,是不是很方便啊.关于如何使用Record Espresso Test功能,以后会单独写一篇文章来说明,其实操作起来也很简单,大家可以先自己去尝试一下.

Hamcrest

上面出现了allOf这个东西,这其实是Hamcrest匹配器里的断言
JUnit4.4引入了Hamcrest框架,Hamcest提供了一套匹配符Matcher,这些匹配符更接近自然语言,可读性高,更加灵活。

Hamcrest 提供了大量被称为“匹配器”的方法。其中每个匹配器都设计用于执行特定的比较操作。Hamcrest的可扩展性很好,让你能够创建自定义的匹配器。最重要的是,JUnit也包含了Hamcrest的核心,提供了对Hamcrest的原生支持,可以直接使用Hamcrest。当然要使用功能齐备的Hamcrest,还是要引入对它的依赖。

Hamcrest匹配器

Hamcrest 提供了很强大的一些api 供我们进行测试断言。

操作类型 相关API 备注
  • 核心
    anything - 总是匹配,如果你不关心测试下的对象是什么是有用的
    describedAs - 添加一个定制的失败表述装饰器
    is - 改进可读性装饰器 - 见下 “Sugar”
  • 逻辑
    allOf - 如果所有匹配器都匹配才匹配,像Java里的&&
    anyOf - 如果任何匹配器匹配就匹配,像Java里的||
    not - 如果包装的匹配器不匹配器时匹配,反之亦然
  • 对象
    equalTo - 测试对象相等使用Object.equals方法
    hasToString - 测试Object.toString方法
    instanceOf, isCompatibleType - 测试类型
    notNullValue, nullValue - 测试null
    sameInstance - 测试对象实例
    Beans:
    hasProperty - 测试JavaBeans属性
  • 集合
    array - 测试一个数组元素test an array’s elements against an array of matchers
    hasEntry, hasKey, hasValue - 测试一个Map包含一个实体,键或者值
    hasItem, hasItems - 测试一个集合包含一个元素
    hasItemInArray - 测试一个数组包含一个元素
  • 数字
    closeTo - 测试浮点值接近给定的值
    greaterThan, greaterThanOrEqualTo, lessThan, lessThanOrEqualTo - 测试次序
  • 文本
    equalToIgnoringCase - 测试字符串相等忽略大小写
    equalToIgnoringWhiteSpace - 测试字符串忽略空白
    containsString, endsWith, startsWith - 测试字符串匹配

这些API 几乎覆盖了我们测试断言的所有情况。再提供良好阅读性的情况下,减少了一些取值、循环、类型判断等代码的编写。

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

推荐阅读更多精彩内容