11.18 区分WebElement、MobileElement、AndroidElement和iOSElement
在Appium自动化测试中,可能有些初学者会对获取控件元素对象的类型存在疑惑,不知道在什么情况下使用什么类型。下面将介绍控件元素对象类型的区别。
" WebElement可以使用所有的Selenium命令。
" MobileElement属于Appium,继承自WebElement,但是又增加了一些Appium特有的功能(如Touch手势)。
" AndroidElement和iOSElement实现了WebElement接口方法,并增加了一些Android和iOS特有的功能(如findByAndroidUiAutomation)。
根据待测手机操作系统平台,可以选择不同的应用,或者根据是否跨平台进行选择。
11.19 区分RemoteWebDriver、AppiumDriver、AndroidDriver和iOSDriver
在Appium自动化测试中,可能有些初学者会对创建什么类型的驱动产生困惑,本节将介绍各个驱动类型的区别。
" RemoteWebDriver:这个驱动来自于Selenium,可以使执行测试的机器和发送测试命令的机器独立开来,中间存在网络请求。Appium是基于客户端/服务器的,所有RemoteWebDriver可以直接初始化会话。但是一般不建议使用,Appium提供了其他驱动,可能在使用上更加方便。
" AppiumDriver:继承自RemoteWebDriver,但是增加了一些特有的功能(如上下文切换)。
" AndroidDriver:继承自AppiumDriver,但是增加了一些特有的功能,如openNtificutions方法,只有在Android设备或者Android模拟器上才使用这个驱动。
" iOSDriver:继承自AppiumDriver,但是增加了一些特有的功能,只有在iOS设备或者iOS模拟器上才使用这个驱动。
在实际的使用场景中,根据手机操作系统不同,建议直接使用AndroidDriver或者iOSDriver。
11.20 在代码中启动服务器
在Appium测试执行时,需要手动启动Appium服务器。在一些并行测试场景下,要启动多个Appium服务器,如果在代码中未使用driver.quit关闭服务器,或者存在其他一些异常,就会出现会话无法创建的情况。Appium官网提供了AppiumDriverLocalService来完成Appium服务器的启动和关闭。这一节讲述如何设置Appium服务器的启动和关闭,可以根据项目要求进行集成。
使用AppiumDriverLocalService的前提条件有以下两个。
" 安装Node.js 7以上版本。
" 通过npm安装Appium服务器。
具体的操作如下。
(1)如果没有指定参数,实现方式如代码清单11-28所示。
代码清单11-28 未指定参数
import io.appium.java_client.service.local.AppiumDriverLocalService; ... AppiumDriverLocalService service = AppiumDriverLocalService.buildDefaultService(); service.start(); ... service.stop(); |
本地环境中可能会在这一步报错。
AppiumDriverLocalService service = AppiumDriverLocalService.buildDefaultService();
这个问题在UNIX/Linux下面比较常见,可能是因为使用的node.js实例与环境变量设置的实例不是同一个,也有可能是Appium node服务导致的(Appium.js版本小于等于1.4.16,Main.js版本大于等于1.5.0)。在这种情况下,建议用户设置NODE_BINARY_PATH(Windows操作系统下指node.exe所在路径,Linux/Mac OS下指node所在路径)和APPIUM_BINARY_PATH(Appium.js和Main.js的执行路径)到环境变量中,也可以在程序中指定。
//appium.node.js.exec.path System.setProperty(AppiumServiceBuilder.NODE_PATH , "the path to the desired node.js executable"); System.setProperty(AppiumServiceBuilder.APPIUM_PATH , "the path to the desired appium.js or main.js"); AppiumDriverLocalService service = AppiumDriverLocalService.buildDefaultService(); |
(2)指定参数,如代码清单11-29所示。
代码清单11-29 指定参数
import io.appium.java_client.service.local.AppiumDriverLocalService; import io.appium.java_client.service.local.AppiumServiceBuilder; import io.appium.java_client.service.local.flags.GeneralServerFlag; ... AppiumDriverLocalService service = AppiumDriverLocalService. buildService(new AppiumServiceBuilder(). withArgument(GeneralServerFlag.TEMP_DIRECTORY, "The_path_to_the_temporary_directory")); 或者 import io.appium.java_client.service.local.AppiumDriverLocalService; import io.appium.java_client.service.local.AppiumServiceBuilder; import io.appium.java_client.service.local.flags.GeneralServerFlag; ... AppiumDriverLocalService service = new AppiumServiceBuilder(). withArgument(GeneralServerFlag.TEMP_DIRECTORY, "The_path_to_the_temporary_directory").build(); |
需要导入以下3个包。
io.appium.java_client.service.local.flags.GeneralServerFlag io.appium.java_client.service.local.flags.AndroidServerFlag io.appium.java_client.service.local.flags.iOSServerFlag |
(3)定义参数。
在有些情况下可能需要使用一些特殊的端口(指定端口)。
new AppiumServiceBuilder().usingPort(4000);
或者使用那些未使用的端口。
new AppiumServiceBuilder().usingAnyFreePort();
使用其他的IP地址。
new AppiumServiceBuilder().withIPAddress("127.0.0.1");
确定日志文件。
import java.io.File; ... new AppiumServiceBuilder().withLogFile(logFile); |
Node.js执行路径,如代码清单11-30所示。
代码清单11-30 Node.js执行路径
import java.io.File; ... new AppiumServiceBuilder().usingDriverExecutable(nodeJSExecutable); |
Main.js执行路径,如代码清单11-31所示。
代码清单11-31 Main.js执行路径
import java.io.File; ... //appium.js is the full or relative path to //the appium.js (v<=1.4.16) or maim.js (v>=1.5.0) new AppiumServiceBuilder().withAppiumJS(new File(appiumJS)); |
确定服务器端的Desired Capabilities,如代码清单11-32所示。
代码清单11-32 确定服务器端的Desired Capabilities
DesiredCapabilities serverCapabilities = new DesiredCapabilities(); ...//the capability filling AppiumServiceBuilder builder = new AppiumServiceBuilder(). withCapabilities(serverCapabilities); AppiumDriverLocalService service = builder.build(); service.start(); ... service.stop(); |
11.21 PageFactory注解
第8章中使用了Page Object和PageFactory两种设计模式。这一节将详细阐述Appium官方关于Page Object和PageFactory的使用,并通过实例加深对它们的认识,以便在实际使用中对这些概念不会产生疑惑并能灵活地根据需求进行设置。更复杂的使用场景参考官方文档。
(1)如代码清单11-33所示,默认设置为WebElement或WebElement 数组,注释方式使用FindBy,元素类型为WebElement。
代码清单11-33 FindBy实例
import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; ... @FindBy(someStrategy) //for browser or web view html UI //also for mobile native applications when other locator strategies are not defined WebElement someElement; @FindBy(someStrategy) //for browser or web view html UI //also for mobile native applications when other locator strategies are not defined List<WebElement> someElements; |
(2)指定具体的定位策略。如代码清单11-34所示,根据Desired Capability中设置的automationName自动化测试引擎的值,针对移动原生应用(Native App),分别使用"@ AndroidFindBy""@ SelendroidFindBy"和"@ iOSFindBy"进行注解,元素类型为AndroidElement、RemoteWebElement以及IOSElement。
代码清单11-34 指定具体的定位策略
import io.appium.java_client.android.AndroidElement; import org.openqa.selenium.remote.RemoteWebElement; import io.appium.java_client.pagefactory.*; import io.appium.java_client.ios.IOSElement; @AndroidFindBy(someStrategy) //for Android UI when Android UI automator is used AndroidElement someElement; @AndroidFindBy(someStrategy) //for Android UI when Android UI automator is used List<AndroidElement> someElements; @SelendroidFindBy(someStrategy) //for Android UI when Selendroid automation is used RemoteWebElement someElement; @SelendroidFindBy(someStrategy) //for Android UI when Selendroid automation is used List<RemoteWebElement> someElements; @iOSFindBy(someStrategy) //for iOS native UI IOSElement someElement; @iOSFindBy(someStrategy) //for iOS native UI List<IOSElement> someElements; |
(3)跨平台的原生App测试实例,如代码清单11-35所示。针对原生App,使用"@AndroidFindBy"和"@iOSFindBy"同时进行注解。元素的类型为MobileElement。
代码清单11-35 跨平台的原生App测试实例
import io.appium.java_client.MobileElement; import io.appium.java_client.pagefactory.*; @AndroidFindBy(someStrategy) @iOSFindBy(someStrategy) MobileElement someElement; @AndroidFindBy(someStrategy) //for the crossplatform mobile native @iOSFindBy(someStrategy) //testing List<MobileElement> someElements; |
(4)全平台的测试实例,如代码清单11-36所示。其中使用"@FindBy""@AndroidFindBy"以及"@iOSFindBy"同时进行注解。元素的类型为RemoteWebElement。
代码清单11-36 全平台的测试实例
import org.openqa.selenium.remote.RemoteWebElement; import io.appium.java_client.pagefactory.*; import org.openqa.selenium.support.FindBy; //the fully cross platform examle @FindBy(someStrategy) //for browser or web view html UI @AndroidFindBy(someStrategy) //for Android native UI @iOSFindBy(someStrategy) //for iOS native UI RemoteWebElement someElement; //the fully cross platform examle @FindBy(someStrategy) @AndroidFindBy(someStrategy) //for Android native UI @iOSFindBy(someStrategy) //for iOS native UI List<RemoteWebElement> someElements; |
(5)用Chained或者Possible定位方式。
" Chained定位方式。使用"@FindBys""@AndroidFindBys"和"@iOSFindBy"进行注解。元素内容通过多种定位方法找到。FindBys相当于在多种定位方式中取交集,如"@FindBys({@FindBy(someStrategy1)""@FindBy(someStrategy2)})"相当于首先根据someStrategy1找到对应元素,然后在这些元素中通过someStrategy2再次查找元素,这类似于driver.findelement(someStrategy1). findelement(someStrategy2),如代码清单11-37和代码清单11-38所示。
代码清单11-37 Chained定位方式之一
import org.openqa.selenium.remote.RemoteWebElement; import io.appium.java_client.pagefactory.*; import org.openqa.selenium.support.FindBys; import org.openqa.selenium.support.FindBy; @FindBys({@FindBy(someStrategy1), @FindBy(someStrategy2)}) @AndroidFindBy(someStrategy1) @AndroidFindBy(someStrategy2) @iOSFindBy(someStrategy1) @iOSFindBy(someStrategy2) RemoteWebElement someElement; @FindBys({@FindBy(someStrategy1), @FindBy(someStrategy2)}) @AndroidFindBy(someStrategy1) @AndroidFindBy(someStrategy2) @iOSFindBy(someStrategy1) @iOSFindBy(someStrategy2) List<RemoteWebElement> someElements; |
代码清单11-38 Chained定位方式之二
importorg.openqa.selenium.remote.RemoteWebElement; importio.appium.java_client.pagefactory.*; importorg.openqa.selenium.support.FindBys; importorg.openqa.selenium.support.FindBy; importstaticio.appium.java_client.pagefactory.LocatorGroupStrategy.CHAIN; @HowToUseLocators(androidAutomation=CHAIN,iOSAutomation=CHAIN) @FindBys({@FindBy(someStrategy1),@FindBy(someStrategy2)}) @AndroidFindBy(someStrategy1)@AndroidFindBy(someStrategy2) @iOSFindBy(someStrategy1)@iOSFindBy(someStrategy2) RemoteWebElementsomeElement; @HowToUseLocators(androidAutomation=CHAIN,iOSAutomation=CHAIN) @FindBys({@FindBy(someStrategy1),@FindBy(someStrategy2)}) @AndroidFindBy(someStrategy1)@AndroidFindBy(someStrategy2) @iOSFindBy(someStrategy1)@iOSFindBy(someStrategy2) List<RemoteWebElement>someElements; |
" Possible定位方式。如代码清单11-39所示,这种定位方式指使用"@FindAll""@AndroidFindAll"和"@iOSFindAll"进行注解。FindAll相当于在多种定位方式中取并集,如"@FindAll{@FindBy(someStrategy1)","@FindBy(someStrategy2)})"相当于取到所有符合someStrategy1和someStrategy2的元素。
代码清单11-39 Possible定位方式
import org.openqa.selenium.remote.RemoteWebElement; import io.appium.java_client.pagefactory.*; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.FindByAll; import static io.appium.java_client.pagefactory.LocatorGroupStrategy.ALL_POSSIBLE; @HowToUseLocators(androidAutomation = ALL_POSSIBLE, iOSAutomation = ALL_POSSIBLE) @FindAll{@FindBy(someStrategy1), @FindBy(someStrategy2)}) @AndroidFindBy(someStrategy1) @AndroidFindBy(someStrategy2) @iOSFindBy(someStrategy1) @iOSFindBy(someStrategy2) RemoteWebElement someElement; @HowToUseLocators(androidAutomation = ALL_POSSIBLE, iOSAutomation = ALL_POSSIBLE) @FindAll({@FindBy(someStrategy1), @FindBy(someStrategy2)}) @AndroidFindBy(someStrategy1) @AndroidFindBy(someStrategy2) @iOSFindBy(someStrategy1) @iOSFindBy(someStrategy2) List<RemoteWebElement> someElements; |
版权声明:51Testing软件测试网获人民邮电出版社和作者授权连载本书部分章节。
任何个人或单位未获得明确的书面许可,不得对本文内容复制、转载或进行镜像,否则将追究法律责任。