Android在SDK中集成了JUnit框架。所以,你可以基于JUNIT框架进行安卓的白盒测试代码开发
* 所有测试代码一定要抛出异常
* A
ndroid创建测试工程的步骤:
* 创建Android Test Project,然后选择原项目
* 测试类必须继承自AndroidTestCase
* 所有的方法都要以test开始
* 所有的异常都要抛出
* 最后运行Android JUnit Test
注:若在原工程测试,则另建立一个测试包,写测试逻辑代码,但要在清单文件中加配置,事例如下:
在AndroidManifest.xml文件中进行配置
1.在
<
manifest>标签中配置指令集
2.在
<
application>标签中配置使用到的类库
public class TestBusiness extends AndroidTestCase {
public void testGetSub() throws Exception{
Business business =new Business();
int sum = business.getSum(2, 3);
//assertEquals(expected, actual):第一个参数expected:指期望的值;第二个参数actual:返回的结果值
assertEquals(5, sum);
}
}
2.测试相关概念
2.1
根据测试的粒度可以划分出几种测试方法
1.
单元测试(方法测试) junit test
是对编码的验证,保证编码无误,也就是保证某个单元(可以是页面,可以是某个流程等等)被正确的编码。
2.
集成测试,几个模块进行测试 integration test
3.
系统测试, system test
2.2
根据测试的力度划分
1.
压力测试 pressure test(12306)
* adb shell
* monkey 2000
2.
冒烟测试 smoke test(重复操作)
3.APP的测试分为许多点,其中的部分:
3.1 软件的权限测试。如连网,发送信息,打开摄像头等操作。
3.2 安装与卸载安全性。如安装是否正确,卸载是否干净等。
3.3 数据安全性。 如密码不会以明文形式显示,密码是否容易被解密等。
3.4 通话安全性处理。如软件在运行过程中,接到电话,是否能够将软件运行状态保存,并优先处理电话。
3.5 UI测试。软件运行效果图是否与设计效果图一致。
3.6 功能测试。是对功能设计的验证,按照设计思路,运行软件,观察各个功能是否能够正确执行。
3.7 后台切换。如将运行中的APP切换到后台之后,当再次打开时是否还为当前显示页面。
3.8 结合测试,是对详细设计的验证,保证各个单元串起来之后,能够完成基本的业务流转。
3.9 用户体验,是对需求分析的验证,保证系统就是用户想要的东西。
4.Android测试框架
4.1 Robotium
毫无疑问,早期Android世界里用得最为广泛的测试框架就是Robotium。它与Selenium的Android版本有类似之处,可以让测试API更简单。
Robotium是一个开源的库,基于JUnit扩展了大量关于Android UI的有效测试方法。它为 Android 应用程序(原生和混合)和web 测试提供了功能强大且可靠的自动化黑盒测试用例。只要源码可用,利用Robotium,你就可以编写功能测试、系统测试和验收测试场景,并执行测试。
//Public void 方法
public void testRecorded() throws Exception {
//等待文本“Hello”显示
if (solo.waitForText("Hello!")) {
//找到R类id标识符为"Sign in"的控件-然后点击它
solo.clickOnView(solo.findViewById("com.twitter.android.R.id.sign_in"));
//找到用户名的控件并输入登陆用户名
solo.enterText((EditText) solo.findViewById("com.twitter.android.R.id.login_username"),"username");
//找到密码的控件并输入登陆密码
solo.enterText((EditText) solo.findViewById("com.twitter.android.R.id.login_password"),"password");
//找到登陆按钮,点击登陆
solo.clickOnView(solo.findViewById("com.twitter.android.R.id.login_login"));
//等待登陆完成
solo.waitForActivity("HomeTabActivity");
}
//激活文本域来编写一个tweet
solo.clickOnView(solo.findViewById("com.twitter.android.R.id.menu_compose_tweet"));
//编辑tweet
solo.enterText((EditText) solo.findViewById("com.twitter.android.R.id.edit"), "Testdroid");
//推送tweet中!
solo.clickOnView(solo.findViewById("com.twitter.android.R.id.composer_post"));
}
Testdroid Recorder 是一个很棒的记录工具,它基于Robotium来创建测试脚本为你带来方便。通过在真正设备上执行实际操作,它可以记录每个步骤或者你执行的动作,然后转换为Javascript供你进一步修改。
此外,你也可以完全下载和使用我们的扩展库——ExtSolo。它包括了一些尚未合并到Robotium的有用方法,比如:
- 支持任意分辨率的x、y点击自动缩放
- 多路径拖拽
- 测试失败时自动截屏
- 模拟位置
- 修改设备语言
- 控制WiFi连接
4.2 uiautomator
Robotium这个框架不错但稍显简单。uiautomator可以让你在测试Android app和游戏时实现更多操作。Google的测试框架(译者注:uiautomator是Google在SDK4.0以后推出的自动化解决方案)支持在一个或多个设备上测试本地Android app用户界面(UI)。uiautomator的另一个优点是它运行JUnit测试用例时有特权,意味着测试用例可以跨进程。它还提供了五个不同的类给开发者使用,包括:
com.android.uiautomator.core.UiCollection;
com.android.uiautomator.core.UiDevice;
com.android.uiautomator.core.UiObject;
com.android.uiautomator.core.UiScrollable;
com.android.uiautomator.core.UiSelector
由于它出现的时间点关系,它只适用于与API级别为16
(译者注:相当于SDK4.1)
或更高版本的Android设备。Uiautomator的另一个缺点是它不支持webview,没有办法到直接获取到Android的UI对象。
//Public void 方法
public void testSignInAndTweet() throws Exception {
//启动应用
getUiDevice().wakeUp();
//点击Home键以保证回到主屏
getUiDevice().pressHome();
//选择“应用”并点击
new UiObject(new UiSelector().description("Apps")).click();
//选择“Twitter”并点击
new UiObject(new UiSelector().text("Twitter")).click();
//定位并选择“登陆”
UiSelector signIn = new UiSelector().text("Sign In");
//如果按钮可用,则点击
UiObject signInButton = new UiObject(signIn);
if (signInButton.exists()) {
signInButton.click();
//设置用户名
new UiObject(new
UiSelector().className("android.widget.EditText").instance(0)).setText("username");
new UiObject(new
UiSelector().className("android.widget.EditText").instance(1)).setText("password");
new UiObject(new UiSelector().className("android.widget.Button").
text("Sign In").instance(0)).click();
//等待登陆处理
getUiDevice().waitForWindowUpdate(null, 2000);
// 等待主窗口
getUiDevice().waitForWindowUpdate(null, 30000);
}
new UiObject(new UiSelector().description("New tweet")).click();
//编辑一个新tweet
new UiObject(new UiSelector().className("android.widget.LinearLayout").instance(8)).
setText("Awesome #Testdroid!");
//推送tweet
new UiObject(new UiSelector().text("Tweet")).click();
}
4.3 Espresso
Espresso是一个最新的Android自动化测试框架,由Google开源,它可以帮助开发和测试推敲UI的设计。Espresso有一个很小、可预测的且容易上手的API,它构建在Android instrumentation 框架的顶层。你可以利用它快速写出简洁可靠的Android UI测试用例。它支持API级别为8(Froyo)、10 (Gingerbread)和 15 (Ice Cream Sandwich)及之后的设备。
它相当可靠,与UI线程同步并且速度快,因为没有必要做任何休眠等待的操作(当app空闲时测试运行在同一个毫秒级别)。不过它同样不支持webview。
public void testEspresso() {
// Check if view with the text 'Hello.' is shown
//如果带有文本“Hello”的控件显示,则点击
onView(withText("Hello.")).check(matches(isDisplayed()));
// R class ID identifier for 'Sign in' - and click it
//找到R类id标识符为"Sign in"的控件-然后点击它
onView(withId(getInstrumentation().getTargetContext().getResources()
.getIdentifier("com.twitter.android:id/sign_in", null, null))).perform(click());
// R class ID identifier for entering username
//找到用户名的控件并输入登陆用户名
onView(withId(getInstrumentation().getTargetContext().getResources()
.getIdentifier("com.twitter.android:id/login_username", null, null))).perform((typeText("username")));
// R class ID identifier for entering password
//找到密码的控件并输入登陆密码
onView(withId(getInstrumentation().getTargetContext().getResources()
.getIdentifier("com.twitter.android:id/login_password", null, null))).perform((typeText("password")));
// R class ID identifier for clicking log in
//找到登陆按钮,点击登陆
onView(withId(getInstrumentation().getTargetContext().getResources()
.getIdentifier("com.twitter.android:id/login_login", null, null))).perform(click());
// Activate the text field to compose a tweet
//激活文本域来编写一个tweet
onView(withId(getInstrumentation().getTargetContext().getResources()
.getIdentifier("com.twitter.android:id/menu_compose_tweet", null, null))).perform(click());
// Type the tweet
//编辑tweet
onView(withId(getInstrumentation().getTargetContext().getResources()
.getIdentifier("com.twitter.android:id/edit", null, null))).perform((typeText(”#Testdroid")));
// Tweeting!
//推送tweet中!
onView(withId(getInstrumentation().getTargetContext().getResources()
.getIdentifier("com.twitter.android:id/composer_post", null, null))).perform(click());
}
4.4 Calabash
Calabash是一个跨平台的自动化测试框架,支持Android、iOS原生和混合的应用测试。Calabash的语法通俗易懂,甚至技术零基础的人也能为运行在各种移动平台上的应用编写和执行自动化验收测试用例。Calabash的测试用例编写是基于Cucumber(译者注:Calabash的底层实现是Cucumber,它是一种BDD测试框架),然后在运行时将脚本转换为Robotium或者Frank。它支持大约80种自然语言命令(控制器),并且可以用Ruby或Java实现新的控制器。
Feature: Login feature
Scenario: As a valid user I can log into my app
I wait for text "Hello"
Then I press view with id "Sign in"
Then I enter text "username" into "login_username"
Then I enter text "password" into "login_password"
Then I wait for activity "HomeTabActivity"
Then I press view with id "menu_compose_tweet"
Then I enter text "Testdroid" into field with id "edit"
Then I press view with id "composer_post"
4.5 Appium
Appium是一个移动自动化测试框架(工具),可支持iOS和Android的原生、混合的移动web应用测试。它内部采用Selenium WebDriver的JSON Wire Protocol来与iOS和Android app交互。它通过驱动uiautomator(API级别高于等于16)和Seledroid(API级别低于16)支持Android,驱动UIAutomation来支持iOS,利用Selenium driver来支持 Android和iOS都有的移动web。
Appium最大的一个优点就是几乎可以使用任何一种语言(比如Java、Objective-C、JavaScript、PHP、Ruby、Python、C#等)来编写Appium脚本,不受工具选择的限制,兼容两个最重要的平台(Android和iOS),安装和配置设备来测试等也自由许多。而且如果你对Selenium很熟悉,那么对你而言使用Appium测试移动app轻而易举。他们使用的是相同的WebDriver,且DesiredCapabilities(译者注:DesiredCapabilities是由客户端发送给Appium服务器端用来告诉服务器去启动哪种会话的一套键值对集合)的使用方式相同。因此在Appium上配置运行一个应用与在Selenium上有非常多相似之处。
它的优点:
- 开源;
- 支持Native App、Hybird App、Web App;
- 支持Android、iOS、Firefox OS;
- Server也是跨平台的,你可以使用Mac OS X、Windows或者Linux;
它的哲理是:
- 用Appium自动化测试不需要重新编译App;
- 支持很多语言来编写测试脚本,Java、Javascript、PHP、Python、C#、Ruby等主流语言;
- 不需要为了自动化测试来重造轮子,因为扩展了WebDriver。(WebDriver是测试WebApps的一种简单、快速的自动化测试框架,所以有Web自动化测试经验的测试人员可以直接上手);
- 移动端自动化测试应该是开源的;
它的设计理念:
- Client/Server架构,运行的时候Server端会监听Client端发过来的命令,翻译这些命令发送给移动设备或模拟器,然后移动设备或模拟器做出响应的反应。正是因为这种架构,所以Client可以使用Appium client libraries多种语言的测试脚本,而且Server端完全可以部署在服务器上,甚至云服务器。
- Session,每个Client连接到Server以后都会有一个Session ID,而且Client发送命令到Server端都需要这个Session ID,因为这个seesion id代表了你所打开的浏览器或者是移动设备的模拟器。所以你甚至可以打开N个Session,同时测试不同的设备或模拟器。
- Desired Capabilities,其实就是一个键值对,设置一些测试的相关信息来告诉Server端,我们需要测试iOS、还是Android,或者换是WebApp等信息。
- Appium Server是Node.js写的,所以可以直接用NPM来进行安装。
- Appium Clients,Mac OS和Win下提供GUI,不需要装Node.js,方便测试人员操作。
相关限制:
- 如果你在Windows使用Appium,你没法使用预编译专用于OS X的.app文件,因为Appium依赖OS X专用的库来支持iOS测试,所以在Windows平台你不能测试iOS Apps。这意味着你只能通过在Mac上来运行iOS测试。
5.1 Monkey测试
Monkey只是检查使用下shell命令即可。
adb shell monkey -p cn.mycommons.testcase -v 50000
5.2 Monkey Runner测试
Monkey Runner提供的是一个python文件,然后调用monkeyrunner命令即可。
$ monkeyrunner test_case.py
# Imports the monkeyrunner modules used by this program
from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice
# Connects to the current device, returning a MonkeyDevice object
print 'wait for device connection.'
device = MonkeyRunner.waitForConnection()
print 'connect device success.'
# Takes a screenshot
result = device.takeSnapshot()
print 'takeSnapshot success.'
# Writes the screenshot to a file
result.writeToFile('./test_case1.png','png')
print 'save image to file success.'
# input user name
device.touch(200,380,'DOWN_AND_UP')
print 'touch user name'
for x in xrange(1,100):
device.press("KEYCODE_DEL",'DOWN_AND_UP')
print 'delete user name'
device.type("admin")
print 'input admin to user name'
# input passwd
device.touch(200,500,'DOWN_AND_UP')
print 'touch password'
for x in xrange(1,100):
device.press("KEYCODE_DEL",'DOWN_AND_UP')
print 'delete password'
device.type("admin")
print 'input admin to password'
# press login button
device.touch(200,680,'DOWN_AND_UP')
print 'press login button'
MonkeyRunner.sleep(2)
# Takes a screenshot
result = device.takeSnapshot()
print 'takeSnapshot success.'
# Writes the screenshot to a file
result.writeToFile('./test_case2.png','png')
print 'save image to file success.'
5.3 UiAutomator测试
54 Espressor测试
package cn.mycommons.testcase;
import android.support.test.espresso.Espresso;
import android.support.test.espresso.action.ViewActions;
import android.support.test.espresso.matcher.ViewMatchers;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class MainActivityEspressoTest {
@Rule
public ActivityTestRule<MainActivity> testRule = new ActivityTestRule<>(MainActivity.class);
@Test
public void testLogin() {
Espresso.onView(ViewMatchers.withContentDescription(R.id.edtUserName)).perform(ViewActions.typeText("admin"));
Espresso.onView(ViewMatchers.withId(R.id.edtPasswd)).perform(ViewActions.typeText("admin"));
Espresso.onView(ViewMatchers.withId(R.id.btnLogin)).perform(ViewActions.click());
}
@Test
public void testLogin1() {
Espresso.onView(ViewMatchers.withId(R.id.edtUserName)).perform(ViewActions.typeText(""));
Espresso.onView(ViewMatchers.withId(R.id.edtPasswd)).perform(ViewActions.typeText(""));
Espresso.onView(ViewMatchers.withId(R.id.btnLogin)).perform(ViewActions.click());
}
@Test
public void testLogin2() {
Espresso.onView(ViewMatchers.withId(R.id.edtUserName)).perform(ViewActions.typeText("abc"));
Espresso.onView(ViewMatchers.withId(R.id.edtPasswd)).perform(ViewActions.typeText(""));
Espresso.onView(ViewMatchers.withId(R.id.btnLogin)).perform(ViewActions.click());
}
@Test
public void testLogin3() {
Espresso.onView(ViewMatchers.withId(R.id.edtUserName)).perform(ViewActions.typeText(""));
Espresso.onView(ViewMatchers.withId(R.id.edtPasswd)).perform(ViewActions.typeText("abc"));
Espresso.onView(ViewMatchers.withId(R.id.btnLogin)).perform(ViewActions.click());
}
@Test
public void testLogin4() {
Espresso.onView(ViewMatchers.withId(R.id.edtUserName)).perform(ViewActions.typeText("abc"));
Espresso.onView(ViewMatchers.withId(R.id.edtPasswd)).perform(ViewActions.typeText("abc"));
Espresso.onView(ViewMatchers.withId(R.id.btnLogin)).perform(ViewActions.click());
}
@Test
public void testLogin5() {
Espresso.onView(ViewMatchers.withId(R.id.edtUserName)).perform(ViewActions.typeText("abcdef"));
Espresso.onView(ViewMatchers.withId(R.id.edtPasswd)).perform(ViewActions.typeText("abcdef"));
Espresso.onView(ViewMatchers.withId(R.id.btnLogin)).perform(ViewActions.click());
}
}
6. 自动化测试总结
-
Monkey
准确来说,这不算是自动化测试,因为其只能产生随机的事件,无法按照既定的步骤操作;
-
Monkeyrunner
优点:操作最为简单,可以录制测试脚本,可视化操作;
缺点:主要生成坐标的自动化操作,移植性不强,功能最为局限,上面代码中已经显示出来,完全使用的数字坐标,移植到另外一个设备,则不能运行。
-
UiAutomator
优点:可以对所有操作进行自动化,操作简单;
缺点:Android版本需要高于4.3,无法根据控件ID操作,相对来说功能较为局限,但也够用了;
-
Espresso
优点:主要针对某一个APK进行自动化测试,APK可以有源码,也可以没有源码,功能强大;
缺点:针对APK操作,因此操作相对复杂;
总结:测试某个APK,可以选择Espresso;测试过程可能涉及多个APK,选择UiAutomator;一些简单的测试,选择Monkeyrunner;