午夜精品人妻久久久-成年美女很黄的网站-在线看片免费人成视久网app-国产精品美女无遮挡一区二区-91精品国产综合久久久久-国产的免费视频又猛又爽又刺激-在线看片免费人成视久网app-久久香蕉国产精品视频-av一区二区三区高清

使用UI Automator 為 Android 應(yīng)用程序編寫自動化測試腳本

本文同步發(fā)布在個人博客 使用UI為應(yīng)用程序編寫自動化測試腳本 – 碼微
本文將介紹如何基于 編寫 UI腳本來自動測試任何應(yīng)用程序 。將編寫一個在設(shè)置界面里添加 Wi-Fi 網(wǎng)絡(luò)的 測試腳本 , 并檢查設(shè)備是否連接成功
UI 介紹
UI 是一個測試框架 , 它允許我們編寫可以與設(shè)備中安裝的任何應(yīng)用程序交互的腳本 。UI不需要訪問應(yīng)用程序源代碼即可工作 。因此,腳本可以導(dǎo)航并與應(yīng)用程序托盤、設(shè)置應(yīng)用程序、第三方應(yīng)用程序或你想要的任何其他應(yīng)用程序交互 。
創(chuàng)建項目
在里創(chuàng)建一個新項目 。將在項目中創(chuàng)建三個不同的文件夾:main:和test 。這是默認的項目結(jié)構(gòu) , 其中main包含應(yīng)用程序代碼,test包含在開發(fā)機器上運行的單元測試,即默認情況下進行的檢測測試,如 UI測試 。
如果是給當(dāng)前的應(yīng)用程序編寫測試腳本口袋妖怪個體值計算器 for android,那么可以在目錄中創(chuàng)建腳本,
但在本文中,我們僅使用 UI腳本創(chuàng)建項目,主要測試另一個應(yīng)用程序 , 所以在開始需要做一些修改:
打開app模塊的build.口袋妖怪個體值計算器 for android,在節(jié)點下添加以下代碼:
sourceSets {androidTest {java.srcDir 'src/main/java'}}
可以同時刪除test和目錄 。同時刪除一些不再使用的資源:
app/src/main/res/values/themes.xml app/src/main/res/values-night/themes.xml app/src/main/res/values/colors.xml
配置好目錄后,讓我們添加所需的依賴項 。我們需要使用而不是添加依賴項ation 。你還可以刪除不會使用的依賴項:
dependencies {implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"implementation 'androidx.test.ext:junit:1.1.3'implementation 'androidx.test:runner:1.4.0'implementation 'androidx.test.uiautomator:uiautomator:2.2.0'}
更改后 , 你build.將如下所示:
plugins {id 'com.android.application'id 'kotlin-android'}android {compileSdkVersion 30buildToolsVersion "30.0.3"defaultConfig {applicationId "com.paceli.wifitest"minSdkVersion 18targetSdkVersion 30versionCode 1versionName "1.0"testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}}compileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility JavaVersion.VERSION_1_8}kotlinOptions {jvmTarget = '1.8'}sourceSets {androidTest {java.srcDir 'src/main/java'}}}dependencies {implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"implementation 'androidx.test.ext:junit:1.1.3'implementation 'androidx.test:runner:1.4.0'implementation 'androidx.test.uiautomator:uiautomator:2.2.0'}
需要手動將標(biāo)簽添加到.xml文件中:
在節(jié)點下添加子節(jié)點,
的name屬性必須定義為.test..
必須和當(dāng)前應(yīng)用包名保持一致
修改后的.xml以下更改:

編寫腳本
創(chuàng)建并配置項目后 , 創(chuàng)建一個新類并添加注釋@(::class),定義為測試運行器 。
與 JUnit 一樣 , 測試方法必須使用@Test. 啟動(初始化)和停止(反初始化)方法必須分別用注釋@和@After 。如下代碼所示:
package com.paceli.wifitestimport android.util.Logimport androidx.test.ext.junit.runners.AndroidJUnit4import org.junit.*import org.junit.runner.RunWith@RunWith(AndroidJUnit4::class)class UiAutomatorOrder {/*** Run before the method with @Test annotation*/@Beforefun before() {Log.d(TAG, "Before")}/*** Run after the method with @Before annotation* and before methods with @After annotation*/@Testfun test() {Log.d(TAG, "Test")}/*** Run after each method with @Test annotation*/@Afterfun after() {Log.d(TAG, "After")}companion object {private const val TAG = "UiAutomatorExample"/*** Run once before other methods from [UiAutomatorOrder] class*/@JvmStatic@BeforeClassfun beforeClass() {Log.d(TAG, "Before Class")}/*** Run once after other methods from [UiAutomatorOrder] class*/@JvmStatic@AfterClassfun afterClass() {Log.d(TAG, "After Class")}}}
運行此示例將產(chǎn)生以下輸出:
Before ClassBeforeTestAfterAfter Class
現(xiàn)在在測試類中創(chuàng)建一個名為的方法并標(biāo)注@Test.
為了點擊按鈕、從屏幕讀取文本、執(zhí)行滑動手勢以及與 UI 的任何其他元素交互,我們需要獲取類的實例
此操作在init函數(shù)中完成
package com.paceli.wifitestimport androidx.test.ext.junit.runners.AndroidJUnit4import androidx.test.platform.app.InstrumentationRegistryimport androidx.test.uiautomator.UiDeviceimport org.junit.Testimport org.junit.runner.RunWith@RunWith(AndroidJUnit4::class)class WifiTest {private val device: UiDeviceinit {val instrumentation = InstrumentationRegistry.getInstrumentation()device = UiDevice.getInstance(instrumentation)}@Testfun validateWifi() {}}
要與屏幕上的元素進行交互,我們需要使用實例獲取對它們的引用 。為此,我們將調(diào)用方法傳遞我們想要與之交互的元素的屬性 。一些屬性,比如文本,對用戶是可見的,但還有一些我們在設(shè)備屏幕上看不到,所以需要使用SDK 中的UI工具($/tools/bin/) 獲取屏幕信息
通過單擊左上角的 按鈕,該工具將顯示當(dāng)前ADB連接的設(shè)備的屏幕截圖和屏幕dump信息 。
可以通過在屏幕截圖或右側(cè)的層次結(jié)構(gòu)視圖中單擊元素來選擇元素 。index, text, -desc,等屬性顯示在Node 視圖中 。
類表示來自屏幕的元素,它的一個實例由方法返回 。為了啟動設(shè)置應(yīng)用,用戶需要在主屏幕上執(zhí)行滾動手勢以啟動應(yīng)用程序列表 , 然后單擊設(shè)置圖標(biāo) 。于是我們得到了一個的實例 , 代表了應(yīng)用列表界面中的設(shè)置圖標(biāo),然后分別調(diào)用了和click方法 。
@Testfun validateWifi() {// Open apps list by scrolling on home screenval workspace = device.findObject(By.res("com.google.android.apps.nexuslauncher:id/workspace"))workspace.scroll(Direction.DOWN, 1.0f)// Click on Settings icon to launch the appval settings = device.findObject(By.res("com.google.android.apps.nexuslauncher:id/icon").text("Settings"))settings.click()}
如果某些元素需要一些時間才能顯示在屏幕上,我們可以使用方法wait,此方法的參數(shù)為和 。下面調(diào)用這個方法打開該 & 部分,然后繼續(xù)到Add 屏幕
// ...// Wait up to 2 seconds for the element be displayed on screenval networkAndInternet = device.wait(Until.findObject(By.text("Network & internet")), 2000)networkAndInternet.click()// Click on element with text "Wi?Fi"val wifi = device.wait(Until.findObject(By.text("Wi?Fi")), 2000)wifi.click()// Click on element with text "Add network"val addNetwork = device.wait(Until.findObject(By.text("Add network")), 2000)addNetwork.click()
在Add 屏幕上,有一個文本字段,用戶必須在其中輸入網(wǎng)絡(luò) SSID 。要在 UI腳本中輸入文本,我們只需要獲取此字段的實例并調(diào)用傳遞我們要輸入的字符串
下面的代碼展示輸入 SSID ,然后單擊保存按鈕添加它 。
// ...// Obtain an instance of UiObject2 of the text fieldval ssidField = device.wait(Until.findObject(By.res("com.android.settings:id/ssid")), 2000)// Call the setText method usingKotlin's property access syntaxval ssid = "AndroidWifi"ssidField.text = ssid//Click on Save buttondevice.findObject(By.res("android:id/button1").text("Save")).click()
為了檢查Wi-Fi是否正確添加以及設(shè)備是否連接到它,可以簡單地檢查一下屏幕上是否顯示,為此,讓我們使用方法 , 該方法返回一個布爾值,指示某個元素當(dāng)前是否正在屏幕上顯示 。
// ...// BySelector matching the just added Wi-Fival ssidSelector = By.text(ssid).res("android:id/title")// BySelector matching the connected statusval status = By.text("Connected").res("android:id/summary")// BySelector matching on entry of Wi-Fi list with the desired SSID and statusval networkEntrySelector = By.clazz(RelativeLayout::class.qualifiedName).hasChild(ssidSelector).hasChild(status)// Perform the validation using hasObject// Wait up to 5 seconds to find the element we're looking forval isConnected = device.wait(Until.hasObject(networkEntrySelector), 5000)Assert.assertTrue("Verify if device is connected to added Wi-Fi", isConnected)
我們還可以選擇使用API 來獲取設(shè)備連接的當(dāng)前 Wi-Fi 的 SSID 。這是可能的,因為 UI腳本作為應(yīng)用程序運行,因此它可以訪問應(yīng)用程序開發(fā)過程中常用的 API,如、系統(tǒng)服務(wù)、等 。
為了能夠獲取 Wi-Fi SSID , 請將這些權(quán)限添加到.xml:

以下方法獲取應(yīng)用程序的(UI腳本)并使用它來獲取的實例并獲取 Wi-Fi SSID 。我們將使用返回值與我們之前添加的網(wǎng)絡(luò)名稱進行比較 。
private fun getCurrentWifiSsid(): String? {val context = InstrumentationRegistry.getInstrumentation().contextval wifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManagerval wifiInfo = wifiManager.connectionInfo// The SSID is quoted, then we need to remove quotesreturn wifiInfo.ssid?.removeSurrounding(""")}
現(xiàn)在測試方法已準(zhǔn)備就緒:
@Testfun validateWifi() {// Open apps list by scrolling on home screenval workspace = device.findObject(By.res("com.google.android.apps.nexuslauncher:id/workspace"))workspace.scroll(Direction.DOWN, 1.0f)// Click on Settings icon to launch the appval settings = device.findObject(By.res("com.google.android.apps.nexuslauncher:id/icon").text("Settings"))settings.click()// Wait up to 2 seconds for the element be displayed on screenval networkAndInternet = device.wait(Until.findObject(By.text("Network & internet")), 2000)networkAndInternet.click()// Click on element with text "Wi?Fi"val wifi = device.wait(Until.findObject(By.text("Wi?Fi")), 2000)wifi.click()// Click on element with text "Add network"val addNetwork = device.wait(Until.findObject(By.text("Add network")), 2000)addNetwork.click()// Obtain an instance of UiObject2 of the text fieldval ssidField = device.wait(Until.findObject(By.res("com.android.settings:id/ssid")), 2000)// Call the setText method usingKotlin's property access syntaxval ssid = "AndroidWifi"ssidField.text = ssid//Click on Save buttondevice.findObject(By.res("android:id/button1").text("Save")).click()// BySelector matching the just added Wi-Fival ssidSelector = By.text(ssid).res("android:id/title")// BySelector matching the connected statusval status = By.text("Connected").res("android:id/summary")// BySelector matching on entry of Wi-Fi list with the desired SSID and statusval networkEntrySelector = By.clazz(RelativeLayout::class.qualifiedName).hasChild(ssidSelector).hasChild(status)// Perform the validation using hasObject// Wait up to 5 seconds to find the element we're looking forval isConnected = device.wait(Until.hasObject(networkEntrySelector), 5000)Assert.assertTrue("Verify if device is connected to added Wi-Fi", isConnected)// Perform the validation using Android APIsval connectedWifi = getCurrentWifiSsid()Assert.assertEquals("Verify if is connected to the Wifi", ssid, connectedWifi)}
要擁有完整的測試腳本,我們需要在執(zhí)行實際測試之前進行一些設(shè)置 。如果你不是在主屏幕時運行腳本,你會注意到拋出了 發(fā)生這種情況是因為我們假設(shè)測試開始時設(shè)備位于主屏幕中 。為了保證這一點,請在@注釋中添加以下設(shè)置方法 。
@Beforefun setUp() {// Press Home key before running the testdevice.pressHome()}
現(xiàn)在,Home在運行測試之前將始終按下該鍵 。你可能還想在測試執(zhí)行后返回主屏幕,為此只需添加另一個帶有@After注釋的方法 , 類似于我們對設(shè)置方法所做的 。
@Afterfun tearDown() {// Press Home key after running the testdevice.pressHome()}
總之 , setUp方法用于確保設(shè)備處于所需的初始狀態(tài) 。方法負責(zé)在運行測試后進行清理,以免影響后續(xù)的方法 。假設(shè)你正在編寫一個向設(shè)備添加密碼的腳本 。必須在拆卸方法中刪除此密碼,否則下一個腳本可能會卡在密碼屏幕中 。
使用 ADB 運行腳本
運行前需要現(xiàn)在 中構(gòu)建APK , 點擊Build -> Make 即可
構(gòu)建完成后通過ADB安裝:
adb install -r -g app-debug.apk
執(zhí)行:
adb shell am instrument -w -e class 'com.paceli.wifitest.WifiTest' com.paceli.wifitest/androidx.test.runner.AndroidJUnitRunnercom.paceli.wifitest.WifiTest — 是要執(zhí)行的測試腳本的類名 。com.paceli.wifitest/androidx.test.runner.AndroidJUnitRunner — 是測試包名和運行器類 , 格式為/.
結(jié)論
希望本文可以幫助你創(chuàng)建自己的測試腳本,提高應(yīng)用程序的測試覆蓋率并減少手動工作
【使用UI Automator 為 Android 應(yīng)用程序編寫自動化測試腳本】本文到此結(jié)束,希望對大家有所幫助 。