Appium 测试参考手册 v1.0
目录
- 引言
1.1 文档目的
1.2 建议使用者
2.Appium 安装
2.1 客户端安装及用途
2.2 Server(服务端)安装及用途
3.脚本编写
3.1 识别(定位)界面元素
3.2 脚本编写规范
4.案例执行
1 引言
1.1 文档的目的
本文档详细介绍如何通过 Appium 客户端来录制脚本代码,以及如何通过其官方 API 去写一个可执行的测试脚本,最后介绍 Appium 的整个测试流程,来帮助测试人员可以迅速进行移动端的自动化测试。
1.2 建议使用者
2 Appium 安装(以下内容的安装适用 Windows)
2.1 客户端安装及用途
安装:请根据个人设备(Windows or Mac)选择合适的安装包,安装过程极其简单,下载完后根据提示操作即可。
安装包地址:https://github.com/appium/appium-desktop/releases/tag/v1.10.0
安装成功后,会有一个 appium 的快捷方式。
用途:它主要是实现了 Appium 功能的 WebDriver 协议的客户端 Library,负责与 Appium 服务器建立连接,并将测试脚本的指令发送给 Appium Server(服务器)。它可抓取 app 上定位信息,辅助我们识别(定位)元素。
2.2 Server(服务端)安装及用途
安装:appium 服务端是基于 node.js 实现的 HTTP 服务器,所以安装 appium 需要先安装 Node,然后才能安装appium服务端,可通过cmd 下输入 npm install -g appium 进行安装。
如果npm install -g appium特别慢,无法下载时,可以用国内的镜像 npm —registry registry.cnpmjs.org install -g appium 安装如果失败可以尝试通过cnpm安装appium,首先通过npm命令安装cnpm
npm install -g cnpm --registry=registry.npm.taobao.org
再通过cnpm安装appium
cnpm install -g appium --no-cache
安装成功后,通过 appium -v 查看当前 appium 版本,直接输入 appium 则启动 appium server 端。
最后,打开 Windows 命令提示符,输入“appium-doctor”命令,如果出现以下提示,说明你 Appium 所需要的各项环境都已准备完成。
特别注意:如果提示:“appium-doctor”不是内部或外部命令,找到 Appium 的安装目录,例如:
C:\Program Files\Appium\node_modules.bin 添加到环境变量 path 下面。
用途:Appium 服务器是 Appium 框架的核心。主要功能是接受 Appium 客户端发起的连接,监听从客户端发送来的命令,将命令发送给 bootstrap.jar 执行,并将命令的执行结果通过 HTTP 应答反馈给 Appium 客户端。
特别注意: 客户端启动手机上的应用时,会开启一个服务,这时就不用启动服务端的服务了。客户端的服务和服务端的服务是完全一样的,只不过客户端启服务是为了我们录制脚本,而服务端则是和Jenkins搭配使用的。
3.测试脚本编写
3.1 识别(定位)界面元素(ReactNative 项目)
一、启动 Appium 客户端,点击 Start Server。如图所示
然后出现如下界面并点击图示放大镜🔍按钮,如下所示:
进入了 Desired Capabilities 参数配置界面,如下所示:
倘若我们已经配置好了 Desired Capabilities, 我们便可以点击 Start Session 启动我们手机端的应用了。
特别注意:配置参数 必须是 Automatic Server 下的 Desired Capabilities。
二、测试 Android 手机的 Desired Capabilities 具体配置
Desired Capabilities: 是一组键值对的集合,它主要通知 Appium 服务器建立需要的 Session, 其中一些设置可以改变 Appium 服务器的运行行为。

补充:unicodeKeyboard 和 resetKeyboard 是为了防止键盘冲突(手机键盘和 Appium 自带的键盘),不配置这两个参数会导致无法输入中文、英文以及特殊符号等问题。
三、点击Start Session,开始录制脚本
Session:Appium的客户端和服务端之间进行通信都必须在一个Session的上下文中进行。客户端在发起通信的时候首先会发送一个叫作“Desired Capabilities”的JSON对象给服务器。服务器收到该数据后,会创建一个session并将session的ID返回到客户端。之后客户端可以用该session的ID发送后续的命令。
点击 Start Recording 后,我们开始录制脚本。点击左侧用户名输入框,然后点击右侧的 Tap 按钮,随后点击 Send Keys,弹出输入框,填入我们的内容,点击弹框上的 Send Keys 即可,等待用户名输入完成。再继续密码的输入操作,和用户名一样的方式操作。最后点击登录按钮,然后点击 Tap, 点击暂停,完成本次录制。得到的脚本如下:
到这儿,恭喜你完成了一次完整的脚本录制。
特别注意: 录制的脚本不可直接拿来当作测试案例。
3.2 脚本编写规范
一、编写脚本
// 以下均是 Java 脚本示例
普通控件(例如:InputItem)定位:
MobileElement elOne = (MobileElement)driver.findElementByAccessibilityId("AccessibilityId的值")
按钮组件(例如:Button)定位:
MobileElement elTwo = (MobileElement) driver.findElementByXPath("//android.view.View[contains(@index, 7)]")
二、脚本案例
package cn.agree.fintech.nice;import java.net.MalformedURLException;import java.net.URL;import org.junit.After;import org.junit.Before;import org.junit.Test;import org.openqa.selenium.remote.DesiredCapabilities;import io.appium.java_client.MobileElement;import io.appium.java_client.android.AndroidDriver;public class AppTest {private AndroidDriver driver;@Before // 对应 api 文档中的 Creatpublic void setUp() throws MalformedURLException {DesiredCapabilities desiredCapabilities = new DesiredCapabilities();desiredCapabilities.setCapability("platformName", "Android");desiredCapabilities.setCapability("appPackage", "com.agree.test");desiredCapabilities.setCapability("appActivity", "com.shellproject.StartActivity");desiredCapabilities.setCapability("deviceName", "Galaxy Note3");desiredCapabilities.setCapability("unicodeKeyboard", "True");desiredCapabilities.setCapability("resetKeyboard", "True");URL remoteUrl = new URL("http://localhost:4723/wd/hub");driver = new AndroidDriver(remoteUrl, desiredCapabilities);}@Testpublic void sampleTest() {// 休眠函数是为了保证脚本的稳定性且必须放在 try catch 中try {Thread.sleep(30000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}// 脚本正文示例MobileElement el1 = (MobileElement) driver.findElementByAccessibilityId("username"); // RN 项目中我们加入的 accessibilityLabel="username"el1.click(); // 点击输入框的空白位置,然后点击 Tapel1.sendKeys("wanghuajian"); // 输入值MobileElement el2 = (MobileElement) driver.findElementByAccessibilityId("password");el2.click();el2.sendKeys("abc123!");MobileElement el3 = (MobileElement) driver.findElementByAccessibilityId("cnfpwd");el3.click();el3.sendKeys("abc123!");MobileElement el4 = (MobileElement) driver.findElementByAccessibilityId("mobile");el4.click();el4.sendKeys("15630257145");MobileElement el5 = (MobileElement) driver.findElementByAccessibilityId("email");el5.click();el5.sendKeys("15630257145@qq.com");MobileElement el6 = (MobileElement) driver.findElementByAccessibilityId("address");el6.click();el6.sendKeys("张江路旁边的哈士奇");MobileElement el7 = (MobileElement) driver.findElementByXPath("//android.view.View[contains(@index, 7)]");el7.click();try {Thread.sleep(5000);} catch (InterruptedException e) {//TODO: handle exceptione.printStackTrace();}}@After // 对应 api 文档中的 Endpublic void tearDown() {driver.quit();}}
三、多个脚本
多个脚本的编写,可以在同级目录新建文件夹,在文件夹内新建文件放置编写的脚本,即可一一测试。
4 案例流程(Jenkins)
一、在终端启动 appium 服务
二、在 Jenkins 上进行如下操作
① 拉取代码
② 构建打包(是把 CAP4M 的工程代码打包成 apk 或者 ipa)
③ 把打好的包安装到手机
④ 单元测试
- 拉取单元测试项目(可从码云、Github、SVN拉取)
- 编译打包
- 进行单元测试
- 出单元测试报告
特别注意:本地执行测试脚本,不做断言处理,我们仅验证脚步编码执行是否畅通。
附件 api 文档
- Creat // 这部分代码 Appium 会帮我们生成。(前提是我们的参数配置正确) ```java // Java DesiredCapabilities desiredCapabilities = new DesiredCapabilities(); desiredCapabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, “10.3”); desiredCapabilities.setCapability(MobileCapabilityType.DEVICE_NAME, “iPhone Simulator”); desiredCapabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, “XCUITest”); desiredCapabilities.setCapability(MobileCapabilityType.APP, “/path/to/ios/app.zip”);
URL url = new URL(“http://127.0.0.1:4723/wd/hub“);
IOSDriver driver = new IOSDriver(url, desiredCapabilities); String sessionId = driver.getSessionId().toString();
- End // 这部分代码 Appium 会帮我们生成。代表着测试结束或完成。```java// Javadriver.quit();
Screenshot // 截屏
// JavaFile scrFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
Timeouts
① Timeouts // 配置特定类型的操作在中止之前可以执行的时间量
// Javadriver.manage().timeouts().pageLoadTimeout(30, TimeUnit.SECONDS);
② Implicit Wait // 设置驱动程序在搜索元素时应等待的时间
// Javadriver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
③ Async Script // 设置允许执行异步执行的异步脚本在中止之前运行的时间(以毫秒为单位)
// Javadriver.manage().timeouts().setScriptTimeout(30, TimeUnit.SECONDS);
- Geolocation
① Get Geolocation // 获取当前位置
// JavaLocation location = driver.location(); // Must be a driver that implements LocationContext
② Set Geolocation // 设置当前位置
// Javadriver.setLocation(new Location(49, 123, 10)); // Must be a driver that implements LocationContext
Find Element // 在页面上寻找一个元素
// JavaMobileElement elementOne = (MobileElement) driver.findElementByAccessibilityId("SomeAccessibilityID");MobileElement elementTwo = (MobileElement) driver.findElementByClassName("SomeClassName");
Find Elements // 在页面上寻找多个元素
// JavaList<MobileElement> elementsOne = (MobileElement) driver.findElementsByAccessibilityId("SomeAccessibilityID");List<MobileElement> elementsTwo = (MobileElement) driver.findElementsByClassName("SomeClassName");
Click // 单击其中心点的元素
// JavaMobileElement el = driver.findElementByAccessibilityId("SomeId");el.click();
Send Keys // 将一系列击键发送到元素
// JavaMobileElement element = (MobileElement) driver.findElementByAccessibilityId("SomeAccessibilityID");element.sendKeys("Hello world!");
Clear // 清楚一个元素的值
// JavaMobileElement element = (MobileElement) driver.findElementByAccessibilityId("SomeAccessibilityID");element.clear();
Text // 返回元素的可见文本 ```java
// Java MobileElement element = (MobileElement) driver.findElementByClassName(“SomeClassName”); String elText = element.getText();
- Name // 获取元素的标记名称```java// JavaList<MobileElement> element = (MobileElement) driver.findElementByAccessibilityId("SomeAccessibilityID");String tagName = element.getTagName();
Attribute // 获取元素属性的值
// JavaList<MobileElement> element = (MobileElement) driver.findElementByAccessibilityId("SomeAccessibilityID");String tagName = element.getAttribute("content-desc");
Selected // 确定是否选择了表单或类似形式的元素(复选框,选择等等)
// JavaMobileElement element = (MobileElement) driver.findElementByAccessibilityId("SomeAccessibilityID");boolean isSelected = element.isSelected();
Enabled // 确定当前是否启用了元素
// JavaMobileElement element = (MobileElement) driver.findElementByAccessibilityId("SomeAccessibilityID");boolean isEnabled = element.isEnabled();
Displayed // 确定当前是否显示元素
// JavaMobileElement element = (MobileElement) driver.findElementByAccessibilityId("SomeAccessibilityID");boolean isDisplayed = element.isDisplayed();
Location // 确定当前元素在页面或屏幕上的位置
// JavaList<MobileElement> element = (MobileElement) driver.findElementByAccessibilityId("SomeAccessibilityID");Point location = element.getLocation();
Size // 确定元素大小
// JavaList<MobileElement> element = (MobileElement) driver.findElementByAccessibilityId("SomeAccessibilityID");Dimension elementSize = element.getSize();
Rect // 获取元素的尺寸和坐标
// JavaList<MobileElement> element = (MobileElement) driver.findElementByAccessibilityId("SomeAccessibilityID");Rectangle rect = element.getRect();
Location in View // 一旦滚动到视图中,确定元素在屏幕上的位置
// java not supported
Submit // 提交 FORM 元素
// JavaMobileElement element = (MobileElement) driver.findElementByClassName("SomeClassName");element.submit();
Scroll // 使用基于手指的动作事件在触摸屏上滚动
// JavaTouchActions action = new TouchActions(driver);action.scroll(element, 10, 100);action.perform();
Long Press // 使用手指运动事件长按触摸屏
// JavaTouchActions action = new TouchActions(driver);action.longPress(element);action.perform();
Move // 手指在屏幕上移动
// JavaTouchActions action = new TouchActions(driver);action.down(10, 10);action.move(50, 50);action.perform();
