Appium学习日记(三)——Windows系统测试桌面应用
一、环境搭建
1-1、WinAppDriver环境搭建
(1)开启开发者选项中的“开发人员模式”
(2)Windows sdk下载安装
下载地址:https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk/
正常安装就行。
(3)winAppDriver下载
下载地址:https://github.com/microsoft/WinAppDriver/releases
任选一个版本,打开如下图,接着根据需求选择合适自己的安装包下载即可。
下载完成后,一键安装即可。
二、使用Appium连接WinAppDriver
2-1、启动WinAppDriver
默认安装路径为:C:\Program Files\Windows Application Driver。点击WinAppDriver.exe。
2-2、启动Appium
(1)打开Appium,设置端口和主机地址,点击“startServer”。
(2)最新的Appium Inspector已经和Appium剥离开了,去官网单独下载一个Appium Inspector。解压后直接点击使用
(3)介绍一下DesiredCapabilities
DesiredCapabilities 携带了一些配置信息,在启动session的时候是必须提供,如启动模式、apk/app配置、package/activity配置、浏览器配置、键盘配置等。
键 | 描述 | 值 |
---|---|---|
automationName | 自动化测试的引擎 | Appium (默认)或者 Selendroid |
platformName | 使用的手机操作系统 | iOS, Android , 或者 FirefoxOS |
platformVersion | 手机操作系统的版本 | 例如 7.1, 4.4 |
deviceName | 使用的手机或模拟器类型 | iPhone Simulator , iPad Simulator , iPhone Retina 4-inch , Android Emulator , Galaxy S4 , 等等… 在 iOS 上,使用 Instruments 的 instruments -s devices 命令可返回一个有效的设备的列表。在 Andorid 上虽然这个参数目前已被忽略,但仍然需要添加上该参数 |
app | 本地绝对路径或远程 http URL 所指向的一个安装包(.ipa,.apk,或 .zip 文件) 。Appium 将其安装到合适的设备上。请注意,如果您指定了 appPackage 和 appActivity 参数(见下文),Android 则不需要此参数了。该参数也与 browserName 不兼容。 | /abs/path/to/my.apk 或 http://myapp.com/app.ipa |
browserName | 做自动化时使用的浏览器 名字。如果是一个应用则只需填写个空的字符串 | 'Safari' 对应 iOS ,'Chrome' , 'Chromium' , 或 'Browser' 则对应 Android |
newCommandTimeout | 用于客户端在退出或者结束 session 之前,Appium 等待客户端发送一条新命令所花费的时间(秒为单位) | 例如 60 |
language | (Sim/Emu-only) 为模拟器设置语言 | 例如 fr |
locale | (Sim/Emu-only) 为模拟器设置所在区域 | 例如 fr_CA |
udid | 连接真机的唯一设备号 | 例如 1ae203187fc012g |
orientation | (Sim/Emu-only) 模拟器当前的方向 | 竖屏 或 横屏 |
autoWebview | 直接转换到 Webview 上下文(context) 。默认值为 false | true, false |
noReset | 在当前 session 下不会重置应用的状态。默认值为 false | true, false |
fullReset | (iOS)删除所有的模拟器文件夹。(Android) 要清除 app 里的数据,请将应用卸载才能达到重置应用的效果。在 Android, 在 session 完成之后也会将应用卸载掉。默认值为 false | true, false |
(4)设置好相关的DesiredCapabilities后,点击Start Session;如下图所示:
2-3、元素定位
方法名 | 参数 | 描述 |
---|---|---|
findElementByName | (String ) 元素name属性 | 通过元素name属性查找,在Android中一般可以用text代替 |
findElementByAndroidUIAutomator | (String ) Ui Automator查找代码 | 使用UI Automator来查找元素 |
findElementByClassName | (String ) 类名,要写全路径:android.weight.TextView | 通过元素类名查找 |
findElementById | (String) 元素id,android:id/title | 通过元素id查找 |
findElementByAccessibilityId | (String) 元素的contentDescription属性 | 通过contentDescription属性查找 |
findEelementByXPath | (String) 元素的XPath | 通过XPath查找 |
findElementByCssSelector | WebView专用 | |
findElementByLinkText | WebView专用 | |
findElementByPartialLinkText | WebView专用 |
注意: 每一个方法对应着一个findElementsBy***
方法,后者返回一个Element集合List,表示一个符合查找规则的一个Element集合。
findElementByName
在Android中,没有合适的方法可以找到控件的Name属性,但是大多数情况下可以用控件的text代替name。
findElementByAndroidUIAutomator
使用UI Automator查找控件
WebElement el = driver.findElementByAndroidUIAutomator("new UiSelector().text(\"Add note\")");
findElementByClassName
WebElement el = driver.findElementByClassName("android.weight.TextView");
findElementById
WebElement el = driver.findElementById("android:id/title");
如果目标设备的API Level低于18则UIAutomatorViewer不能获得对应的Resource ID,只有等于大于18的时候才能使用。
findElementByAccessibilityId
WebElement el = driver.findElementByAccessibilityId("menu_add_note_description");
Accessibility ID在Android上面就等同于contentDescription
findEelementByXPath
WebElement el = driver.findElementByXPath("//android.widget.TextView[contains(@text,'Add note')]");
xPath是一种路径,在uiautomatorviewer中可以查看当前页面的元素层级,XPath就是用来描述这种层级关系的一种路径表达方式,
findElementByCssSelector
这个方法是针对WebView容器下面的控件定位的,因为现在针对的是Native App暂时还没有用到,所以先标记下,今后需要的时候加上去。
findElementByLinkText
这个方法是针对WebView容器下面的控件定位的,因为现在针对的是Native App暂时还没有用到,所以先标记下,今后需要的时候加上去。
findElementByPartialLinkText
这个方法是针对WebView容器下面的控件定位的,因为现在针对的是Native App暂时还没有用到,所以先标记下,今后需要的时候加上去。
UI Automater定位元素
UI Automator中主要通过UISelector类查找元素
通过文本信息定位
返回值 | 方法名 | 说明 | 用法 |
---|---|---|---|
UiSelector | text(String text) | 根据“控件text属性的内容”构造出UiSelector对象 | 例如,一个控件text的值是“发现”,UiSelector s = new UiSelector().text(“发现”); |
UiSelector | textContains(String text) | 根据“控件text属性包含的内容”构造出UiSelector对象 | 同上例子:UiSelector s = new UiSelector().textContains(“现”); |
UiSelector | textMatches(String regex) | 根据“控件text属性正则表达式的内容”构造出UiSelector对象 | 正则表达式语法参考网上资料 |
UiSelector | textStartsWith(String text) | 根据“控件text属性开始的内容”构造出UiSelector对象 | 同上例子:UiSelector s = new UiSelector().textStartsWith(“发”); |
通过description定位
返回值 | 方法名 | 说明 |
---|---|---|
UiSelector | description(String desc) | 根据“控件content-desc属性的内容”构造出UiSelector对象 |
UiSelector | descriptionContains(String desc) | 包含** |
UiSelector | descriptionMatches(String regex) | 正则 |
UiSelector | descriptionStartsWith(String desc) | 以**开始 |
通过ResourceId定位
返回值 | 方法名 | 说明 |
---|---|---|
UiSelector | resourceId(String id) | 根据资源id获取对象,例如:UiSelector s = new UiSelector().resourceId(“com.tencent.mm:id/b8m”) |
UiSelector | resourceIdMatches(String regex) | 根据资源id的正则表达式获取对象 |
通过类名定位
返回值 | 方法名 | 说明 |
---|---|---|
UiSelector | className(String className) | 根据控件类名找到对象 |
UiSelector | classNameMatches(String regex) | 根据类名的正则表达式获取对象 |
UiSelector | instance(int instance) | 找到一个拥有相同属性的对象集合中的对象,例如:UiSelector s = new UiSelector().className(“android.widget.TextView”).instance(1);可以找到页面层级中第二个类名为TextView的元素 |
UiSelector | index(int index) | 用法和上面的instance差不多,谷歌的原文说这个方法是unreliable的,推荐使用instance方法 |
通过层级关系
返回值 | 方法名 | 说明 |
---|---|---|
UiSelector | fromParent(UiSelector s) | 获取同一个父控件的其他子控件,即获取兄弟控件 |
UiSelector | getFromParent(UiSelector s) | 获取同一个父控件的其他子控件,即获取兄弟控件 |
UiSelector | childSelector(UiSelector s) | 获取子控件 |
UiSelector | getChild(UiSelector s) | 获取子控件 |
使用JUnit组织测试用例
使用JUnit来组织Appium测试用例,可以添加@Before
、@Test
或者@After
等注解来使测试代码的运行变得更加灵活。
比如在开始测试之前,需要配置Capability和连接Appium服务器,而且一般需要安装应用或者打开应用的某个Activity。这时候可把这些操作放到一个方法中,并且方法添加@Before
注解,则在运行的时候,@Before
方法会先于@Test
方法执行。
同理,测试结束后需要关闭session,回收资源等等,可以把这些操作放到一个@After
方法中,@After
方法在所有@Test
方法结束之后执行。
显式等待和隐式等待
有时候,由于网络或者其他原因,页面跳转之后,某些元素没有立即显示出来,此时查找元素会失败。这种情况就需要引入显式等待
和隐式等待
。
线程等待
直接使用Thread.sleep();
隐式等待
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
全局等待30秒,不管元素是否已经加载。
显式等待
WebDriverWait
显示等待,显示等待时间可以通过 WebDriverWait
和 util
来决定,比如这个timeOut
是60,如果该元素60s以内出现就不在等待。
WebDriverWait wait = new WebDriverWait(driver, 60);
WebElement e= wait.until(new ExpectedCondition<WebElement>() {
@Override
public WebElement apply(WebDriver d) {
return d.findElement(By.id("q"));
}
})
以下为在Visual Studio下的三个例子。
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Enums;
using OpenQA.Selenium.Appium.Service;
using OpenQA.Selenium.Appium.Windows;
using System;
using System.Threading;
namespace PSDemoStartApp
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void StartApplication()
{
var capabilities = new AppiumOptions();
capabilities.AddAdditionalCapability(MobileCapabilityType.App, "D:/Program Files/Notepad++/notepad++.exe");
capabilities.AddAdditionalCapability(MobileCapabilityType.PlatformName, "Windows");
capabilities.AddAdditionalCapability(MobileCapabilityType.DeviceName, "WindowsPC");
//start the driver
var driver = new WindowsDriver<WindowsElement>(new Uri("http://127.0.0.1:4723/wd/hub"), capabilities);
driver.Close();
driver.Dispose();
}
}
}
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Enums;
using OpenQA.Selenium.Appium.Service;
using OpenQA.Selenium.Appium.Windows;
using System;
using System.Threading;
namespace PSDemoStartApp
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void StartApplication()
{
var capabilities = new AppiumOptions();
capabilities.AddAdditionalCapability(MobileCapabilityType.App, "D:/Program Files/Notepad++/notepad++.exe");
capabilities.AddAdditionalCapability(MobileCapabilityType.PlatformName, "Windows");
capabilities.AddAdditionalCapability(MobileCapabilityType.DeviceName, "WindowsPC");
//start the driver
//var driver = new WindowsDriver<WindowsElement>(new Uri("http://127.0.0.1:4723/wd/hub"), capabilities);
var appiumLocalServer = new AppiumServiceBuilder().UsingAnyFreePort().Build();
appiumLocalServer.Start();
var driver = new WindowsDriver<WindowsElement>(appiumLocalServer, capabilities);
Thread.Sleep(2000);
driver.Close();
driver.Dispose();
}
}
}
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Enums;
using OpenQA.Selenium.Appium.Service;
using OpenQA.Selenium.Appium.Windows;
using System;
using System.Threading;
namespace PSDemoStartApp
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void FindElementTest()
{
var driver = StartNTApplication();
var elementFound = driver.FindElementByName("文件(F)");
Assert.IsTrue(elementFound.Text == "文件(F)");
var elementFound2 = driver.FindElement(By.Name("文件(F)"));
Assert.IsTrue(elementFound.Text == elementFound2.Text);
driver.CloseApp();
}
public WindowsDriver<WindowsElement> StartNTApplication()
{
var capabilities = new AppiumOptions();
capabilities.AddAdditionalCapability(MobileCapabilityType.App, "C:/Windows/System32/notepad.exe");
capabilities.AddAdditionalCapability(MobileCapabilityType.PlatformName, "Windows");
capabilities.AddAdditionalCapability(MobileCapabilityType.DeviceName, "WindowsPC");
//start the driver
//var driver = new WindowsDriver<WindowsElement>(new Uri("http://127.0.0.1:4723/wd/hub"), capabilities);
var appiumLocalServer = new AppiumServiceBuilder().UsingAnyFreePort().Build();
appiumLocalServer.Start();
var driver = new WindowsDriver<WindowsElement>(appiumLocalServer, capabilities);
//Thread.Sleep(2000);
//driver.Close();
//driver.Dispose();
return driver;
}
}
}