您好,登錄后才能下訂單哦!
本文內(nèi)容:
Android及IOS基于TestNG的代碼結(jié)構(gòu)示例,
常用的元素定位方式的介紹
部分項(xiàng)目中使用的功能代碼示例
Appium日志的具體分析
部分需要理解的概念
Android示例代碼(TestNG):
public class AndroidTest { protected AndroidDriver<MobileElement> driver; URL url = new URL("http://127.0.0.1:4723/wd/hub"); @BeforeSuite public void setUp() throws Exception { DesiredCapabilities capabilities = new DesiredCapabilities(); capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "AndroidPhone"); capabilities.setCapability(AndroidMobileCapabilityType.APP_PACKAGE, "com.example.dev"); capabilities.setCapability(AndroidMobileCapabilityType.APP_ACTIVITY, ".MainActivity"); capabilities.setCapability(MobileCapabilityType.NO_RESET, true); ...... driver = new AndroidDriver<>(url, capabilities); } @AfterSuite public void tearDown() throws Exception { if (driver != null) { driver.quit(); } } @Test public void test_001(){}//Test Case One @Test public void test_002(){}//Test Case Two }
IOS示例代碼(TestNG):
public abstract class IOSTest { protected IOSDriver<MobileElement> driver; URL url = new URL("http://127.0.0.1:4723/wd/hub"); @BeforeSuite public void setUp() throws Exception { DesiredCapabilities capabilities = new DesiredCapabilities(); capabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, "iOS"); capabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, "10.3"); capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "My iPhone 6"); capabilities.setCapability(MobileCapabilityType.UDID, "c4ef2cbc8a1b39123838a8ad9fdea45547e44c20"); capabilities.setCapability(IOSMobileCapabilityType.BUNDLE_ID, "com.example.dev"); ...... driver = new IOSDriver<>(url, capabilities); } }
常用的元素定位方式:
Android:
By.id | View的id,最好用 | com.hello.uat:id/et_reg_mobile android:id/button1 button1 |
By.classname | View的類型名 | android.widget.TextView android.widget.Button |
By.xpath | 根據(jù)element的屬性及DOM層級(jí)查找,比較不穩(wěn)定 | By.xpath(".//*[@text='Title']")根據(jù)text屬性查找, 一般對(duì)于索引不確定的元素采用 |
MobileBy. AndroidUIAutomator | Android自帶UIAutomator框架的定位方式 |
IOS:
MobileBy. AccessibilityId | 組件的AccessibilityId,最好用 | 一般同組件的name屬性,使用Inspector獲得 |
By.classname | 組件的類型名 | XCUIElementTypeStaticText XCUIElementTypeButton |
By.xpath | 根據(jù)element的屬性及DOM層級(jí)查找,比較不穩(wěn)定。 | By.xpath(".//*[@name='Title']")根據(jù)name屬性查找,一般都可以用AccessibilityId替代,因?yàn)閚ame和AccessibilityId一般是一樣的 更精準(zhǔn)可以帶上classname: //XCUIElementTypeStaticText[@name="Hello"] 一般對(duì)于索引不確定又沒有AccessibilityId的元素采用 |
MobileBy. IosUIAutomation | IOS自帶框架UIAutomation的定位方式 |
AccessibilityId
appium移除了在Selenium上常用的name和tagname定位方式
IOS上表示Accessibility Identifier,通常和Name、Label屬性相同
Android上表示content description
交互操作
MobileElement:交互的對(duì)象,對(duì)應(yīng)于Android中的一個(gè)View對(duì)象
點(diǎn)擊:element.click();
輸入:element.sendKeys("text");
滑動(dòng):driver.swipe()
點(diǎn)擊:driver.tap();
等待方式:
//隱式等待
//全局設(shè)定查找元素時(shí)的等待時(shí)間,找不到會(huì)嘗試10秒內(nèi)間隔查找,找到了繼續(xù)
//這里設(shè)定的值對(duì)driver的整個(gè)生命周期起作用,從現(xiàn)在開始到下次修改,每次查找都是10秒。
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
//顯式等待
//僅僅這次,指明最多等待5秒,指明結(jié)束條件為找到元素id=bt_next。超時(shí)拋出TimeoutException。
WebElement e = new WebDriverWait(driver, 5).until(
ExpectedConditions.presenceOfElementLocated(By.id("bt_next")));
//強(qiáng)行等待
//線程直接暫停,適合于單純的等待。不查找元素
Thread.sleep(3000)
DesiredCapabilities
這是一個(gè)key-value的集合,用于告訴服務(wù)器建立連接的設(shè)備信息,不同平臺(tái)有所不同。appium支持許多的屬性設(shè)置,可以到官網(wǎng)做個(gè)全面的了解。
MobileCapabilityType:不同平臺(tái)共用
AndroidMobileCapabilityType:Android專用
IOSMobileCapabilityType:IOS專用
Android一般至少需要:
1、platformName
2、platformVersion
3、deviceName
4、appPackage
5、appActivity
IOS一般至少需要:
1、platformName
2、platformVersion
3、deviceName
4、udid
5、bundleId
部分代碼示例
//對(duì)于彈出的權(quán)限對(duì)話框,如果找到Allow按鈕則一直點(diǎn)擊Allow。 protected void grantPermissionIfPrompt() { int count = 0;//at most five times grant MobileElement allowBtn = findElement(By.id(ConstConfig.ALLOW_BUTTON)); while (allowBtn != null && count++ < 5) {//at most 5 time to try allowBtn.click(); allowBtn = findElement(By.id(ConstConfig.ALLOW_BUTTON)); } }
//向上滑動(dòng)列表,直到找到目標(biāo)元素 static public MobileElement scrollDownUnitFound(AndroidDriver<MobileElement> driver, String content) throws Exception { int width = driver.manage().window().getSize().getWidth(); int height = driver.manage().window().getSize().getHeight(); int count = 0; String xpathStr = createXpathDesc(content); do { if (driver.findElementsByXPath(xpathStr).size() > 0) { //in case of the element is still hiding, or the element is showing only a little part of it. driver.swipe(width / 2, height / 2, width / 2, height / 3, 600); Thread.sleep(1000);//sleep 1 second to avoid getting the element in the wrong position. return driver.findElementsByXPath(xpathStr).get(0); } driver.swipe(width / 2, height / 2, width / 2, height / 4, 600); count++; } while (count < 20); return null; }
//生成xpath字符串 static public String createXpathDesc(String content) { return ".//*[@text='" + content + "']"; }
//滑動(dòng)找到元素,并點(diǎn)擊其左側(cè)的CheckBox。CheckBox沒有id,也不適合用classname,因?yàn)椴恢朗堑趲讉€(gè),只能借助相鄰元素坐標(biāo)偏移。 MobileElement el = AndroidHelper.scrollDownUnitFound(driver, voteTarget); if (el != null) {//find the checkbox to the left of the founded target. int targetX = el.getLocation().getX() - 20; int targetY = el.getLocation().getY() + 1;//event +0 is working System.out.println("coordinates:x = " + targetX + " y = " + targetY); driver.tap(1, targetX, targetY, 300); }
日志分析(Appium on AWS Device Farm):
Appium是Client/Server結(jié)構(gòu)的,所以每一次操作都是以Client向Server發(fā)送http請(qǐng)求開始,以Server向Client發(fā)送http響應(yīng)結(jié)束。查閱日志可以按這個(gè)規(guī)律過濾不必要的信息。
Android日志分析:
driver.findElement(By.id("compose_button")).click();
(Adnroid)findElement操作日志:
2017-09-19 04:06:27:278 - [HTTP] --> POST /wd/hub/session/acfa31ae-321f-4ee1-a34b-294667393f8f/element {"using":"id","value":"compose_button"} 2017-09-19 04:06:27:278 - [debug] [MJSONWP] Calling AppiumDriver.findElement() with args: ["id","compose_button","acfa31ae-321f-4ee1-a34b-294667393f8f"] 2017-09-19 04:06:27:279 - [debug] [BaseDriver] Valid locator strategies for this request: xpath, id, class name, accessibility id, -android uiautomator 2017-09-19 04:06:27:279 - [debug] [BaseDriver] Valid locator strategies for this request: xpath, id, class name, accessibility id, -android uiautomator 2017-09-19 04:06:27:279 - [debug] [BaseDriver] Waiting up to 3000 ms for condition 2017-09-19 04:06:27:280 - [debug] [AndroidBootstrap] Sending command to android: {"cmd":"action","action":"find","params":{"strategy":"id","selector":"compose_button","context":"","multiple":false}} 2017-09-19 04:06:27:310 - [AndroidBootstrap] [BOOTSTRAP LOG] [debug] Got data from client: {"cmd":"action","action":"find","params":{"strategy":"id","selector":"compose_button","context":"","multiple":false}} 2017-09-19 04:06:27:310 - [AndroidBootstrap] [BOOTSTRAP LOG] [debug] Got command of type ACTION 2017-09-19 04:06:27:310 - [AndroidBootstrap] [BOOTSTRAP LOG] [debug] Got command action: find 2017-09-19 04:06:27:311 - [AndroidBootstrap] [BOOTSTRAP LOG] [debug] Finding 'compose_button' using 'ID' with the contextId: '' multiple: false 2017-09-19 04:06:27:311 - [AndroidBootstrap] [BOOTSTRAP LOG] [debug] Using: UiSelector[INSTANCE=0, RESOURCE_ID=com.example.hello:id/compose_button] 2017-09-19 04:06:27:329 - [AndroidBootstrap] [BOOTSTRAP LOG] [debug] Returning result: {"status":0,"value":{"ELEMENT":"9"}} 2017-09-19 04:06:27:330 - [debug] [AndroidBootstrap] Received command result from bootstrap 2017-09-19 04:06:27:330 - [debug] [MJSONWP] Responding to client with driver.findElement() result: {"ELEMENT":"9"} 2017-09-19 04:06:27:344 - [HTTP] <-- POST /wd/hub/session/acfa31ae-321f-4ee1-a34b-294667393f8f/element 200 53 ms - 87
(Android)click操作日志:
2017-09-19 04:06:27:348 - [HTTP] --> POST /wd/hub/session/acfa31ae-321f-4ee1-a34b-294667393f8f/element/9/click {"id":"9"} 2017-09-19 04:06:27:348 - [debug] [MJSONWP] Calling AppiumDriver.click() with args: ["9","acfa31ae-321f-4ee1-a34b-294667393f8f"] 2017-09-19 04:06:27:359 - [debug] [AndroidBootstrap] Sending command to android: {"cmd":"action","action":"element:click","params":{"elementId":"9"}} 2017-09-19 04:06:27:375 - [AndroidBootstrap] [BOOTSTRAP LOG] [debug] Got data from client: {"cmd":"action","action":"element:click","params":{"elementId":"9"}} 2017-09-19 04:06:27:391 - [AndroidBootstrap] [BOOTSTRAP LOG] [debug] Got command of type ACTION 2017-09-19 04:06:27:391 - [AndroidBootstrap] [BOOTSTRAP LOG] [debug] Got command action: click 2017-09-19 04:06:27:876 - [debug] [AndroidBootstrap] Received command result from bootstrap 2017-09-19 04:06:27:876 - [debug] [MJSONWP] Responding to client with driver.click() result: true 2017-09-19 04:06:27:880 - [HTTP] <-- POST /wd/hub/session/acfa31ae-321f-4ee1-a34b-294667393f8f/element/9/click 200 529 ms - 76
IOS日志分析:
driver.findByAccessibilityId("Compose").click();
(IOS)findElement操作日志:
2017-09-19 07:19:36:259 - [HTTP] --> POST /wd/hub/session/bbb16560-a4c7-43fc-a5d0-dea9057dff93/element {"using":"accessibility id","value":"Compose"} 2017-09-19 07:19:36:259 - [debug] [MJSONWP] Calling AppiumDriver.findElement() with args: ["accessibility id","Compose","bbb16560-a4c7-43fc-a5d0-dea9057dff93"] 2017-09-19 07:19:36:260 - [debug] [XCUITest] Executing command 'findElement' 2017-09-19 07:19:36:260 - [debug] [BaseDriver] Valid locator strategies for this request: xpath, id, name, class name, -ios predicate string, -ios class chain, accessibility id 2017-09-19 07:19:36:260 - [debug] [BaseDriver] Waiting up to 3000 ms for condition 2017-09-19 07:19:36:261 - [debug] [JSONWP Proxy] Proxying [POST /element] to [POST http://127.0.0.1:8100/session/3FAA9ECD-8EE9-49BC-B1F9-010B03B9821B/element] with body: {"using":"accessibility id","value":"Compose"} 2017-09-19 07:19:37:826 - [debug] [JSONWP Proxy] Got response with status 200: {"value":{"ELEMENT":"E56F3927-08E6-4ECD-B3E4-05F30198FA6A","type":"XCUIElementTypeButton","label":"Compose"},"sessionId":"3FAA9ECD-8EE9-49BC-B1F9-010B03B9821B","status":0} 2017-09-19 07:19:37:829 - [debug] [MJSONWP] Responding to client with driver.findElement() result: {"ELEMENT":"E56F3927-08E6-4ECD-B3E4-05F30198FA6A","type":"XCUIElementTypeButton","label":"Compose"} 2017-09-19 07:19:37:843 - [HTTP] <-- POST /wd/hub/session/bbb16560-a4c7-43fc-a5d0-dea9057dff93/element 200 1570 ms - 171
(IOS)click操作日志:
2017-09-19 07:19:37:869 - [HTTP] --> POST /wd/hub/session/bbb16560-a4c7-43fc-a5d0-dea9057dff93/element/E56F3927-08E6-4ECD-B3E4-05F30198FA6A/click {"id":"E56F3927-08E6-4ECD-B3E4-05F30198FA6A"} 2017-09-19 07:19:37:870 - [MJSONWP] Driver proxy active, passing request on via HTTP proxy 2017-09-19 07:19:37:870 - [debug] [XCUITest] Executing command 'proxyReqRes' 2017-09-19 07:19:37:870 - [debug] [JSONWP Proxy] Proxying [POST /wd/hub/session/bbb16560-a4c7-43fc-a5d0-dea9057dff93/element/E56F3927-08E6-4ECD-B3E4-05F30198FA6A/click] to [POST http://127.0.0.1:8100/session/3FAA9ECD-8EE9-49BC-B1F9-010B03B9821B/element/E56F3927-08E6-4ECD-B3E4-05F30198FA6A/click] with body: {"id":"E56F3927-08E6-4ECD-B3E4-05F30198FA6A"} 2017-09-19 07:19:39:829 - [debug] [JSONWP Proxy] Got response with status 200: {"status":0,"id":"E56F3927-08E6-4ECD-B3E4-05F30198FA6A","value":"","sessionId":"3FAA9ECD-8EE9-49BC-B1F9-010B03B9821B"} 2017-09-19 07:19:39:829 - [JSONWP Proxy] Replacing sessionId 3FAA9ECD-8EE9-49BC-B1F9-010B03B9821B with bbb16560-a4c7-43fc-a5d0-dea9057dff93 2017-09-19 07:19:39:834 - [HTTP] <-- POST /wd/hub/session/bbb16560-a4c7-43fc-a5d0-dea9057dff93/element/E56F3927-08E6-4ECD-B3E4-05F30198FA6A/click 200 1960 ms - 118
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。