且说正文之前,还是先说说Android单元测试的意义或者说为什么我们要进行Android的单元测试?
为什么要进行单元测试?
单元测试可以帮助我们程序员将bug消灭在萌芽期,为后续的集成测试减少时间.大家可以看一下这篇文章,可能会说服你:为什么要进行烦人的单元测试?
如果那篇文章还不能说服你,那我只能使用杀手锏了~~
大家可以去各大招聘网站上看看,大部分公司都要求开发人员会编写测试用例或使用框架或工具进行测试,并且大公司要求更甚.
这下还有什么好说的吗?单元测试是一个硬要求,即便你不喜欢单元测试,但是如果你想进入一个理想的公司,这是必备的一个技能,所以无论如何你都要去学习的,反正技多不压身嘛.
那么,如果你想学习单元测试,从什么地方找学习资源呢?
- Android官网(https://developer.android.com/training/testing/start/index.html)
- 官网的中文翻译,这是一系列文章,同时加了作者自己的见解
- android官网上关于Espresso的一整套教程
- 也可以看我这篇文章来入门
但不管是通过上面哪个途径,主要说的都是通过Espresso框架进行单元测试.
当然也有其他的一些测试框架,大家也可以去尝试,但是Espresso框架是google官方大力推荐的一套测试框架,所以无论如何都要学习一下的.另外,自Android Studio2.2版本开始,google就为Espresso框架内置了一个图形化界面,用来自动生成单元测试代码.在下一篇文章,我会介绍如何利用这种图形化界面进行单元测试代码的编写.
下面正式开始
首先第一步要做的就是集成Espresso测试环境,非常简单,在你要测试的Module的gradle里添加如下两个依赖:
testCompile ‘junit:junit:4.12‘
androidTestCompile ‘com.android.support.test.espresso:espresso-core:2.2.2‘, {
exclude group: ‘com.android.support‘, module: ‘support-annotations‘
}
第一个是junit依赖,这个依赖Android Studio一般都会默认配置,所以你只需要配置第二个依赖即可.
举个栗子:
@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,这个控件必须是可见的
这是以上所有方法中通用的要求,当然有些方法还有额外的要求,比如必须要先获取焦点等,
说到这里,我们梳理一下我们学习了哪些东西:
1,如何根据id或文本或其他方法找到具体控件
2,如何让这个控件执行相关操作
3,一些使用注意事项
所以,现在我们完全可以自己写一个完整的测试用例了.
先来个gif图:
上面的动图测试的流程为:打开软件,滑动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功能,以后会单独写一篇文章来说明,其实操作起来也很简单,大家可以先自己去尝试一下.