上一篇文章:
Android自動(dòng)化測試--Espresso使用
相比上一篇文章所講的Espresso使用妄田,本文所講的自動(dòng)化測試UI Automator最顯著的特點(diǎn)就是,可以與多個(gè)app進(jìn)行交互脚曾。
UI Automator 能夠運(yùn)行在 Android 4.3 (API 18) ?及以上的版本本讥。
使用
首先我們在Android Studio中新建一個(gè)項(xiàng)目鲁冯,取名為UIAutomatorTests。同時(shí)刪除自動(dòng)生成的一些文件堵漱,最終目錄結(jié)構(gòu)如下:
接下來我們看看如何一步一步的使用Espresso,首先在?根目錄的 build.gradle 文件中添加下面的引入示惊。
ext {
buildToolsVersion = "24.0.1"
supportLibVersion = "24.2.0"
uiautomatorVersion = "2.1.1"
runnerVersion = "0.5"
rulesVersion = "0.5"
}
在app目錄中的build.gradle 文件中添加下面的引入,根據(jù)提示點(diǎn)擊Sync Now钧汹。
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
// App dependencies
compile 'com.android.support:appcompat-v7:' + rootProject.supportLibVersion;
compile 'com.android.support:support-annotations:' + rootProject.supportLibVersion;
androidTestCompile 'com.android.support:support-annotations:' + rootProject.supportLibVersion;
androidTestCompile 'com.android.support.test:runner:' + rootProject.runnerVersion;
// UiAutomator Testing
androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:' + rootProject.uiautomatorVersion;
androidTestCompile 'org.hamcrest:hamcrest-integration:1.3'
}
接下來我們在AutomatorTest.class
中編寫測試代碼拔莱,為了測試多個(gè)app塘秦,這里我們選擇上篇文章中的EspressoTests和手機(jī)中的設(shè)置app动看。
在EspressoTests中我們輸入數(shù)據(jù)點(diǎn)擊計(jì)算菱皆,然后通過設(shè)置app為手機(jī)更換一個(gè)一個(gè)鈴聲挨稿。詳細(xì)的測試代碼如下:
package me.shihao.uiautomatortests;
import android.content.Context;
import android.content.Intent;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SdkSuppress;
import android.support.test.runner.AndroidJUnit4;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject;
import android.support.test.uiautomator.UiScrollable;
import android.support.test.uiautomator.UiSelector;
import android.support.test.uiautomator.Until;
import android.support.v7.widget.RecyclerView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.ScrollView;
import android.widget.TextView;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.*;
@RunWith(AndroidJUnit4.class)
@SdkSuppress(minSdkVersion = 18)
public class AutomatorTest {
private static final String PACKAGE_ESPRESSOTESTS = "me.shihao.espressotests";
private static final String PACKAGE_SETTING = "com.android.settings";
@Test
public void testEspressoTestsApp() throws Exception {
//初始化一個(gè)UiDevice對象
UiDevice mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
// 點(diǎn)擊home鍵,回到home界面
mDevice.pressHome();
String launcherPackage = mDevice.getLauncherPackageName();
assertThat(launcherPackage, notNullValue());
mDevice.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)), 3);
// 啟動(dòng)espressotests App
Context context = InstrumentationRegistry.getContext();
Intent intent = context.getPackageManager().getLaunchIntentForPackage(PACKAGE_ESPRESSOTESTS);
// 清除以前的實(shí)例
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
context.startActivity(intent);
// 等待應(yīng)用程序啟動(dòng)
mDevice.wait(Until.hasObject(By.pkg(PACKAGE_ESPRESSOTESTS).depth(0)), 3);
//通過id找到輸入框一
UiObject edt1 = mDevice.findObject(new UiSelector().resourceId("me.shihao.espressotests:id/editText")
.className(EditText.class));
//往里面輸入字符2
edt1.setText("2");
//通過id找到輸入框二
UiObject edt2 = mDevice.findObject(new UiSelector().resourceId("me.shihao.espressotests:id/editText2")
.className(EditText.class));
//往里面輸入5
edt2.setText("5");
//通過文本"計(jì)算"找到按鈕
UiObject btn = mDevice.findObject(new UiSelector().text("計(jì)算").className(Button.class));
//執(zhí)行點(diǎn)擊事件甩十,計(jì)算結(jié)果
btn.click();
//通過id找到顯示結(jié)果的textview
UiObject tvResult = mDevice.findObject(new UiSelector().resourceId("me.shihao.espressotests:id/textView")
.className(TextView.class));
//判斷結(jié)果與預(yù)期是否匹配
assertEquals(tvResult.getText(), "計(jì)算結(jié)果:7");
//通過文本"RecycleView"找到按鈕
UiObject btnRecycleView = mDevice.findObject(new UiSelector().text("RecycleView").className(Button.class));
//執(zhí)行點(diǎn)擊事件跳轉(zhuǎn)到另一個(gè)界面
btnRecycleView.click();
//通過id找到recycleview
UiScrollable recycleview = new UiScrollable(new UiSelector()
.className(RecyclerView.class)
.resourceId("me.shihao.espressotests:id/recycleview"));
//滑動(dòng)到底部
recycleview.flingForward();
//滑動(dòng)到頂部
recycleview.flingBackward();
UiObject item5 = recycleview.getChild(new UiSelector().text("Item 5"));
//點(diǎn)擊Item 5侣监,然后會(huì)彈出一個(gè)對話框
item5.click();
//通過文本"確定"找到對話框中的確定按鈕
UiObject btnConfirm = mDevice.findObject(new UiSelector().text("確定").className(Button.class));
//點(diǎn)擊確定關(guān)閉對話框
btnConfirm.click();
//另外一種方式找到Item 2
UiObject item = mDevice.findObject(new UiSelector()
.className(RecyclerView.class)
.resourceId("me.shihao.espressotests:id/recycleview")
.childSelector(new UiSelector().text("Item 2")));
//點(diǎn)擊彈出對話框
item.click();
//點(diǎn)擊返回關(guān)閉對話框
mDevice.pressBack();
}
@Test
public void testSettingApp() throws Exception {
//初始化一個(gè)UiDevice對象
Context context = InstrumentationRegistry.getContext();
UiDevice mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
//回到home界面
mDevice.pressHome();
// 啟動(dòng)設(shè)置
Intent intent = context.getPackageManager().getLaunchIntentForPackage(PACKAGE_SETTING);
// 清除以前的實(shí)例
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
context.startActivity(intent);
//通過id找到scrollview
UiScrollable scrollview = new UiScrollable(new UiSelector().className(ScrollView.class).resourceId("com" +
".android.settings:id/dashboard"));
//滑動(dòng)到底部
scrollview.flingForward();
//通過文本找到關(guān)于手機(jī)
UiObject aboutPhone = scrollview.getChild(new UiSelector().text("關(guān)于手機(jī)"));
//點(diǎn)擊跳轉(zhuǎn)到手機(jī)信息界面
aboutPhone.click();
//通過description找到向上返回的ImageButton
UiObject ibtnBack = mDevice.findObject(new UiSelector().className(ImageButton.class).description("向上導(dǎo)航"));
//點(diǎn)擊返回
ibtnBack.click();
//滑動(dòng)到包含"提示音和通知"的地方
scrollview.scrollTextIntoView("提示音和通知");
//通過顯示的文本找到控件
UiObject notify = scrollview.getChild(new UiSelector().text("提示音和通知"));
//點(diǎn)擊跳轉(zhuǎn)到下一個(gè)界面
notify.click();
//通過顯示的文本"手機(jī)鈴聲"找到控件
UiObject sound = mDevice.findObject(new UiSelector().text("手機(jī)鈴聲"));
//點(diǎn)擊跳轉(zhuǎn)到鈴聲對話框
sound.click();
//通過id找到鈴聲列表
UiScrollable listview = new UiScrollable(new UiSelector().className(ListView.class).resourceId
("android:id/select_dialog_listview"));
//活動(dòng)到包含"Beat Plucker"處
listview.scrollTextIntoView("Beat Plucker");
//通過顯示的文本找到該項(xiàng)
UiObject beat = listview.getChild(new UiSelector().text("Beat Plucker"));
//執(zhí)行點(diǎn)擊選中鈴聲
beat.click();
//通過文本"確定"找到對話框中的確定按鈕
UiObject btnConfirm = mDevice.findObject(new UiSelector().text("確定").className(Button.class));
//點(diǎn)擊確定關(guān)閉對話框
btnConfirm.click();
//通過id找到顯示結(jié)果的TextView
UiObject tvSound = mDevice.findObject(new UiSelector().resourceId("android:id/summary").className(TextView
.class));
//比較與預(yù)期結(jié)果是否一致
assertEquals(tvSound.getText(), "Beat Plucker");
//點(diǎn)擊home鍵
mDevice.pressHome();
//點(diǎn)擊最近應(yīng)用鍵
mDevice.pressRecentApps();
//通過類名找到顯示最近app的控件TaskStackView
UiScrollable taskStackView = new UiScrollable(new UiSelector().className("com.android.systemui.recents.views" +
".TaskStackView"));
//滑動(dòng)到包含"EspressoTests"處
taskStackView.scrollTextIntoView("EspressoTests");
//通過顯示的文本找到item
UiObject espressoTestsApp = taskStackView.getChild(new UiSelector().text("EspressoTests"));
//點(diǎn)擊切換到前面的espressoTestsApp
espressoTestsApp.click();
}
}
運(yùn)行效果如下:
UI Automator Viewer使用
從上面的測試代碼可以看到邑蒋,我們需要首先知道目標(biāo)控件的一些屬性值,然后再圍繞我們的目標(biāo)屬性構(gòu)建一個(gè)匹配規(guī)則钱慢。?而實(shí)際中我們并不知道app的實(shí)現(xiàn)束莫,控件的屬性并不是那么明顯,或者并沒有那么容易獲取到览绿,這時(shí)穗慕,我們可以使用Android提供的uiautomatorviewer工具幫助我們進(jìn)行分析。
接下來我們就講一下如何使用怀各,Android Studio中點(diǎn)擊Tools >> Android >> Android Device Monitor
下面顯示的就是界面术浪,最左邊會(huì)顯示連接的設(shè)備
點(diǎn)擊會(huì)截圖分析設(shè)備當(dāng)前顯示界面布局
然后右邊會(huì)顯示布局結(jié)構(gòu)以及view詳細(xì)的信息沥曹。
在實(shí)際使用中,我用了幾個(gè)真機(jī)測試都會(huì)報(bào)這個(gè)錯(cuò)誤
Error obtaining UI hierarchy Error while obtaining UI hierarchy XML file: com.android.ddmlib.SyncException: Remote object doesn't exist!
僵腺,也一直沒找到原因壶栋,到后面使用Genymotion并不會(huì)出現(xiàn)這個(gè)問題,有知道的小伙伴琉兜,可以告訴我毙玻,一定非常感謝。
接下來我們就看一看如何使用壓力測試梧疲,歡迎查看下一篇文章: