热门IT资讯网

Appium的代码实例及日志分析

发表于:2024-11-21 作者:热门IT资讯网编辑
编辑最后更新 2024年11月21日,本文内容:Android及IOS基于TestNG的代码结构示例,常用的元素定位方式的介绍部分项目中使用的功能代码示例Appium日志的具体分析部分需要理解的概念Android示例代码(TestNG):

本文内容:

  1. Android及IOS基于TestNG的代码结构示例,

  2. 常用的元素定位方式的介绍

  3. 部分项目中使用的功能代码示例

  4. Appium日志的具体分析

  5. 部分需要理解的概念


Android示例代码(TestNG):

public class AndroidTest {    protected AndroidDriver 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 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.idView的id,最好用com.hello.uat:id/et_reg_mobile
android:id/button1
button1
By.classnameView的类型名android.widget.TextView
android.widget.Button
By.xpath根据element的属性及DOM层级查找,比较不稳定By.xpath(".//*[@text='Title']")根据text属性查找,
一般对于索引不确定的元素采用
MobileBy.
AndroidUIAutomator

Android自带UIAutomator框架的定位方式

IOS:

MobileBy.
AccessibilityId
组件的AccessibilityId,最好用一般同组件的name属性,使用Inspector获得
By.classname组件的类型名XCUIElementTypeStaticText
XCUIElementTypeButton
By.xpath根据element的属性及DOM层级查找,比较不稳定。

By.xpath(".//*[@name='Title']")根据name属性查找,一般都可以用AccessibilityId替代,因为name和AccessibilityId一般是一样的
更精准可以带上classname:
//XCUIElementTypeStaticText[@name="Hello"]
一般对于索引不确定又没有AccessibilityId的元素采用
MobileBy.
IosUIAutomation

IOS自带框架UIAutomation的定位方式


AccessibilityId

appium移除了在Selenium上常用的name和tagname定位方式

IOS上表示Accessibility Identifier,通常和Name、Label属性相同

Android上表示content description



交互操作

MobileElement:交互的对象,对应于Android中的一个View对象

点击:element.click();

输入:element.sendKeys("text");

滑动:driver.swipe()

点击:driver.tap();


等待方式:

//隐式等待

//全局设定查找元素时的等待时间,找不到会尝试10秒内间隔查找,找到了继续

//这里设定的值对driver的整个生命周期起作用,从现在开始到下次修改,每次查找都是10秒。

driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);


//显式等待

//仅仅这次,指明最多等待5秒,指明结束条件为找到元素id=bt_next。超时抛出TimeoutException。

WebElement e = new WebDriverWait(driver, 5).until(

ExpectedConditions.presenceOfElementLocated(By.id("bt_next")));


//强行等待

//线程直接暂停,适合于单纯的等待。不查找元素

Thread.sleep(3000)


DesiredCapabilities

这是一个key-value的集合,用于告诉服务器建立连接的设备信息,不同平台有所不同。appium支持许多的属性设置,可以到官网做个全面的了解。


MobileCapabilityType:不同平台共用

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



部分代码示例

//对于弹出的权限对话框,如果找到Allow按钮则一直点击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));    }}


//向上滑动列表,直到找到目标元素static public MobileElement scrollDownUnitFound(AndroidDriver 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 + "']";}


//滑动找到元素,并点击其左侧的CheckBox。CheckBox没有id,也不适合用classname,因为不知道是第几个,只能借助相邻元素坐标偏移。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结构的,所以每一次操作都是以Client向Server发送http请求开始,以Server向Client发送http响应结束。查阅日志可以按这个规律过滤不必要的信息。


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 uiautomator2017-09-19 04:06:27:279 - [debug] [BaseDriver] Valid locator strategies for this request: xpath, id, class name, accessibility id, -android uiautomator2017-09-19 04:06:27:279 - [debug] [BaseDriver] Waiting up to 3000 ms for condition2017-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 ACTION2017-09-19 04:06:27:310 - [AndroidBootstrap] [BOOTSTRAP LOG] [debug] Got command action: find2017-09-19 04:06:27:311 - [AndroidBootstrap] [BOOTSTRAP LOG] [debug] Finding 'compose_button' using 'ID' with the contextId: '' multiple: false2017-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 bootstrap2017-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 ACTION2017-09-19 04:06:27:391 - [AndroidBootstrap] [BOOTSTRAP LOG] [debug] Got command action: click2017-09-19 04:06:27:876 - [debug] [AndroidBootstrap] Received command result from bootstrap2017-09-19 04:06:27:876 - [debug] [MJSONWP] Responding to client with driver.click() result: true2017-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 id2017-09-19 07:19:36:260 - [debug] [BaseDriver] Waiting up to 3000 ms for condition2017-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 proxy2017-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-dea9057dff932017-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


0