一烘嘱、關(guān)于安卓自動化測試
關(guān)于測試自動化金字塔萝快,金字塔底端是最基礎(chǔ)的單元測試川蒙,再往上是系統(tǒng)接口測試贡翘,再往上就是UI自動化測試蹈矮。UI自動化測試能夠按照寫好的用例在機(jī)器上模擬用戶操作實(shí)現(xiàn)一系列復(fù)雜的操作過程,能夠極大地解放QA的工作量鸣驱,特別是在回歸驗(yàn)證以及安卓兼容性測試方面意義較大泛鸟。所以,UI自動化測試方法還是一個(gè)QA需要掌握基本的技能之一踊东。對于安卓UI自動化測試來說北滥,常用的框架有MonkeyRunner、Robotium闸翅、uiautomation再芋、Appium等,其中Roboitium是較為常用的框架之一坚冀,我們就從Robotium開始學(xué)習(xí)吧济赎。(之前用的orange框架,發(fā)現(xiàn)公司里基本都不用這個(gè)框架了记某,沒人維護(hù)司训,于是便轉(zhuǎn)向基礎(chǔ)的Robotium框架學(xué)習(xí),重構(gòu)自動化代碼)液南。Robotium框架可以在無需源碼僅有apk的情況下進(jìn)行測試壳猜。
二、Robotium自動化測試的原理
Robotium是基于instrumentation的二次封裝贺拣。Roboiutm分查找控件和點(diǎn)擊控件兩大類操作蓖谢。查找控件的原理是:在waitter的waitForView()方法里面通過java反射獲得視圖捂蕴,并匹配所需要的控件,查找并返回控件對象闪幽。點(diǎn)擊控件的原理是:查找到的控件對象解析包裝成MotionEvent啥辨,基于Instrumentation框架,通過InputManager注入事件盯腌。調(diào)用類組織結(jié)構(gòu)如下圖簡示:
三溉知、第一個(gè)Robotium自動化工程
話不多說,開始Robotium自動化測試工程吧腕够!
環(huán)境準(zhǔn)備:
操作系統(tǒng):windows
android SDK
IDE:eclipse+ADT插件
不需源碼级乍,只要一個(gè)apk作為被測對象。(需要打包apk時(shí)生成的R文件)這里我們用網(wǎng)易云閱讀的apk作為被測對象帚湘。
3.1 apk重命名&安裝被測應(yīng)用
Robotium框架需要被測應(yīng)用和測試工程需要使用相同的簽名玫荣,這樣才可以使用Instrumentation把被測應(yīng)用啟動起來。我們本機(jī)eclipse使用的簽名文件可以從window-> Preferences->Android->Build中查看大诸,一般情況下一般存放于C:\Users\yourUserName.android目錄下捅厂。如下圖所示:
從圖中看來我的Eclipse工程使用的是C盤下的debug.keystore文件,那么需要將被測apk用此keystore文件重簽名然后裝到真機(jī)或者測試機(jī)上就OK了资柔。
重簽名可使用重簽名工具re-sign.jar
3.2 新建Robotium自動化測試工程
1.新建工程
在Eclipse上點(diǎn)擊File->New->Other->Android->Android Test Project焙贷,命名后點(diǎn)擊Next,Test Target選擇“This Project”,然后點(diǎn)Next直至Finish為止贿堰。注:可能有時(shí)候會出現(xiàn)Eclipse報(bào)Null Pointer Exception錯誤辙芍,解決方案可參考。
2.添加Robotium Jar包
右鍵該項(xiàng)目羹与,選擇property然后選擇java build path, 選擇 Add External JARs,選擇下到的robotium-solo-5.1.jar故硅。(注:Robotium可從網(wǎng)上下載Jar包)
3.新建測試用例
在項(xiàng)目下新建一個(gè)package,在package下新建一個(gè)測試類纵搁,該類繼承自ActivityInstrumentationTestCase2契吉。如下圖簡示:
注意到這里我新建了另一個(gè)package,里面只放了R.java文件诡渴,不能與測試文件放在同一個(gè)package下(會報(bào)錯)捐晶。而com.pris.test這個(gè)package下新建了一個(gè)測試類FirstTestCase。FirstTestCase.java的代碼如下:
package com.pris.test;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.robotium.solo.Solo;
import android.app.Instrumentation;
import android.content.Intent;
import android.test.ActivityInstrumentationTestCase2;
import android.view.View;
import android.widget.Button;
import junit.framework.Assert;
import other.R;
@SuppressWarnings("rawtypes")
public class FirstTestCase extends ActivityInstrumentationTestCase2 {
private static final String TARGET_PACKAGE_ID = "com.netease.pris";
private static final String LAUNCHER_ACTIVITY_FULL_CLASSNAME = "com.netease.pris.activity.MainGridActivity";
private static Class launcherActivityClass;
private Solo solo;
static {
try {
launcherActivityClass = Class.forName(LAUNCHER_ACTIVITY_FULL_CLASSNAME);
} catch (ClassNotFoundException e){
throw new RuntimeException(e);
}
}
@SuppressWarnings({ "unchecked", "deprecation" })
public FirstTestCase(){
super(TARGET_PACKAGE_ID,launcherActivityClass);
}
@Before
protected void setUp() throws Exception {
super.setUp();
/*第一套初始化方法妄辩,適用于主界面就是MainActivity的情況惑灵,只要下面一行代碼就可以
this.solo = new Solo(getInstrumentation(), getActivity());*/
//第二套初始化方法,適用于主界面不是MainActivity的情況眼耀,使用Intent打開應(yīng)用程序主界面
Instrumentation instrumentation = getInstrumentation();
this.solo = new Solo(instrumentation);
final String targetPackage = instrumentation.getTargetContext()
.getPackageName();
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setClassName(targetPackage,
"com.netease.pris.activity.MainGridActivity");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
instrumentation.getTargetContext().startActivity(intent);
}
@Test
public void testLogin() {
boolean isOk = solo.waitForActivity("com.netease.pris.activity.MainGridActivity", 5000);
Assert.assertTrue("沒有調(diào)起應(yīng)用", isOk);
solo.sleep(3000);
//進(jìn)入左欄賬號頁面
boolean isFindAccountButton = solo.waitForView(R.id.account_btn, 1, 2000);
Assert.assertTrue("沒有找到賬號控件", isFindAccountButton);
View account_button = solo.getView(R.id.account_btn);
solo.clickOnView(account_button);
//點(diǎn)擊登陸按鈕英支,打開登陸頁面
boolean isLoginButton = solo.waitForView(R.id.button_login, 1, 2000);
Assert.assertTrue("沒有找到登陸控件", isLoginButton);
}
@After
public void tearDown() throws Exception {
solo.finishOpenedActivities();
}
代碼結(jié)構(gòu)較簡單,包括一些靜態(tài)資源哮伟,一個(gè)構(gòu)造函數(shù)干花,一個(gè)solo初始化方法妄帘,一個(gè)核心的測試方法,最后一個(gè)銷毀solo對象的結(jié)束方法池凄。這里需要強(qiáng)調(diào)的是solo初始化setUp方法抡驼。有兩套初始化方法,分別適用于主界面是否就是MainActivity的情況肿仑。這里我用的網(wǎng)易云閱讀的apk進(jìn)行測試致盟,它的MainActivity是SplashActivity(是一個(gè)廣告頁),沒有廣告或者廣告播放結(jié)束后進(jìn)入主界面尤慰,所以直接寫solo = new Solo(instrumentation,activity)的話是無法正確創(chuàng)建solo對象的馏锡。需要用Intent去打開應(yīng)用程序主界面。然后solo對象的話使用solo = new(instrumentation)構(gòu)造方法創(chuàng)建伟端。
4.修改Manifest.xml文件
主要修改的地方是instrumentation中指定TargetPackage為待測試的包名杯道,此外檢查manifest中的package是否就是本測試工程的包名。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.pris.test" //這里需是本測試工程的包名
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="7" />
<instrumentation
android:name="android.test.InstrumentationTestRunner"
android:targetPackage="com.netease.pris" /> //這里是被測應(yīng)用程序的包名
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<uses-library android:name="android.test.runner" />
</application>
</manifest>
5.執(zhí)行測試用例
在測試用例文件上右擊责蝠,選擇run as Android Junit Test蕉饼,就可以進(jìn)行測試了。運(yùn)行結(jié)束玛歌,在Eclipse左右會顯示運(yùn)行結(jié)果。如果出錯擎椰,可以去查看logcat支子,排查錯誤的原因。