此文章 偏向于实操
appium支持在不同平台上(windows,linux、mac) - 使用测试脚本(支持不同语言C #,json,Ruby等等) - 模拟测试各个平台的app(web、windows、mobile等)运行情况,支持ios和安卓平台上的原生应用,web应用和混合运用
appium类库封装了标准Selenium客户端类库,为用户提供所有常见的JSON格式selenium命令以及额外的移动设备控制相关的命令,如多点触控手势和屏幕朝向。
官网:http://appium.io/docs/cn/about-appium/intro/
这里主要是windows平台下的安装和使用。
使用各个系统自带的自动化框架可以让appium 独立运行
把各种系统本身提供的框架包装进一套 API —— WebDriver API 中
WebDriver(也叫「Selenium WebDriver」)。
在WebDriverAPI中,规定了一个客户端-服务器协议(称为 JSON Wire Protocol),按照这种客户端-服务器架构,可以使用任何语言编写的客户端向服务器发送适当的 HTTP 请求。
Appium & WebDriver 客户端在技术上而言不是「测试框架」,而是「自动化程序库」


Appium 的核心一个是暴露 REST API 的 WEB 服务器。它接受来自客户端的连接,监听命令并在移动设备上执行,答复 HTTP 响应来描述执行结果
Appium 是一个用 Node.js 写的服务器。可以从源码构建安装或者从 NPM 直接安装:
$ npm install -g appium
$ appium
Appium 客户端,不同客户端支持不同语言,来发起http命令。比如postman,Appium server gui等等
自动化始终在一个会话的上下文中执行。
通过客户端程序库以各自的方式发起与服务器的会话,但最终都会发给服务器一个 POST /session 请求,请求中包含一个被称作「预期能力(Desired Capabilities)」的 JSON 对象。这时服务器就会开启这个自动化会话,并返回一个用于发送后续命令的会话 ID。
预期能力(Desired Capabilities)是一些发送给 Appium 服务器的键值对集合(比如 map 或 hash),它告诉服务器我们想要启动什么类型的自动化会话。
也有许多能力(Capabilities)可以修改服务器在自动化过程中行为。例如,我们可以将 platformName 能力设置为 iOS,以告诉 Appium 我们想要 iOS 中的应用会话,而不是 Android 或者 Windows 应用会话
安装 npm添加链接描述, https://nodejs.org/en/
安装Appium:Appium 有命令行版本和桌面版本。可以两个都装上,桌面版本:在appium官网下载并安装Appium。命令行版本,管理员模式运行cmd:npm install -g appium 可能需要设置网络代理。
安装 node.js, https://nodejs.org/en/download/
安装 UIRecorder ,https://github.com/Microsoft/WinAppDriver/releases
安装结束后,在cmd界面输入“appium-doctor”检查是否缺少东西。
电脑启用“开发者模式”
添加环境变量

安装各平台应用的驱动程序,通过驱动程序,Appium server才能将我们的测试命令翻译给Appium客户端以及应用
桌面app,下载链接https://github.com/appium/appium-inspector/releases

web应用,由Appium Pro托管。直接打开网页就可以 https://inspector.appiumpro.com/
这两个应用程序有完全相同的功能,开网页版会更容易,并节省磁盘空间,同时可以保持多个标签打开!
需要注意的是Appium Inspector设计为默认使用Appium 2.0 (W3C WebDriver协议)。因此,如果是从Appium Desktop(默认情况下设计为使用Appium 1.x)迁移,可能会有一些不同的地方
解决办法
解决办法:
成功解决之后再次测试appium-doctor,提示全部ok
✔ ANDROID_HOME is set to "C:\Microsoft\AndroidSDK\25"
✔ JAVA_HOME is set to "C:\Program Files\Microsoft\jdk-11.0.12.7-hotspot\bin."
✔ ADB exists at C:\Microsoft\AndroidSDK\25\platform-tools\adb.exe
✔ Android exists at C:\Microsoft\AndroidSDK\25\tools\android.bat
✔ Emulator exists at C:\Microsoft\AndroidSDK\25\tools\emulator.exe
✔ Android Checks were successful.
✔ All Checks were successful
(node:25404) Warning: Accessing non-existent property 'padLevels' of module exports inside circular dependency
(Use `node --trace-warnings ...` to show where the warning was created)
一个简单的自动化程序主要分为启动、交互。
不管哪个客户端,启动的步骤和所需要的参数都是一样的。
启动前要设置初始化参数,告知服务器启动哪一个应用app,在哪个平台,用哪个版本
交互要确认交互的对象 - 哪一个ui元素,交互的动作,以及如何判断动作结束
AppiumOptions是Appium中一个关键类
在启动应用时可以设置这些类中的这些参数 http://appium.io/docs/cn/writing-running-appium/caps/#appium-capability
| keyName | 描述 | Value |
|---|---|---|
| appiumOptions.App | 应用 | exe本地绝对路径_或_应用id |
| appiumOptions.PlatformName | 操作系统 | “windows”; |
| appiumOptions.DeviceName | 设备类型 | “windowsPC”; |
上面这三个是Windows应用必须要设置的
| keyName | 描述 | Value |
|---|---|---|
| platformName | 使用的手机操作系统 | iOS, Android等 |
| platformVersion | 手机操作系统的版本 | 例如 7.1, 4.4 |
| deviceName | 使用的手机或模拟器类型 | adb devices命令查看当前设备名称。在 Andorid 上虽然这个参数目前已被忽略,但仍然需要添加上该参数。 为确保在连接多个设备时连接到正确的 Android 设备,请使用 udid 功能。 |
| appActivity | 指从你的包中所要启动的 Android acticity。他通常需要在前面添加. (例如 使用 .MainActivity 代替 MainActivity) | MainActivity, .Settings |
| appPackage | 运行的 Android 应用的包名 | com.example.android.myApp, com.android.settings |
| app | 本地绝对路径_或_远程 http URL 所指向的一个安装包(.ipa,.apk,或 .zip 文件)。Appium 将其安装到合适的设备上。请注意,如果您指定了 appPackage 和 appActivity 参数(见下文),Android 则不需要此参数了。该参数也与 browserName 不兼容。 | /abs/path/to/my.apk 或 http://myapp.com/app.ipa |
如果是WPF程序,在该控件的Automation里面可以找到对应的设置,比如Name,AccessibilityID。
在inspector 里面,可以看到界面元素具体的值
http://appium.io/docs/en/commands/element/find-elements/index.html#selector-strategies
| method | 描述 | Value |
|---|---|---|
| ID | Native element identifier. resource-id for android; name for iOS. | |
| AccessibilityID | AutomationID | |
| Name | 组件的名称 | |
| class Name | ui组件的类型 | BUTTON,MENUBAR等等 |
| xpath | 路径定位,依赖元素绝对路径或相关属性定位。不太推荐这种方式 |
Appium inspector是一个软件,通过他可以查看界面上的所有元素信息,比如某个按键的名字、控件类型、xy坐标,等等
通过简单设置Appium inspector中的参数,快速启动应用,一方面为后面定位控件元素做好准备,一方面可以通过启动应用所需要的参数,参考到自动化程序中。
管理员模式启动Appium server,
可以管理员启动桌面版Appium Server GUI.exe,输入Host127.0.0.1和端口4723。
也可以通过管理员模式启动cmd,输入appium,启动命令行版本
因为inspector(或者其他客户端)与 应用(或者模拟器)之间的通讯交互都需要通过Appium server来传达与翻译。所以要先启动Appium server。
后续交互历史记录在Appium server界面也可以看到详细信息。


管理员模式启动Appium Inspector,设置Remote path
如果Appium版本是1.x 而inspector 版本是新的,就需要设置远端路径 - Remote path 为/wd/hub
如果Appium 版本是2.x 则不用设置
Appium版本为 v1.22.3,所以需要设置
Capabilities
Start Session
自动化测试程序是模拟应用启动、运行,以及其他的一些特定操作。
所以基于inpector启动应用的流程,我们可以在自己加一些操作,比如输入字符串,保存文档等。
我这里是对自己写的WPF应用进行自动化测试,先在文本框中输入字符串,然后保存文档。

MSTest 测试项目,或者单元测试项目项目上右键 - NuGet包管理 - 搜索并添加NuGet包,需要添加以下三个:
- Microsoft.WinAppDriver.Appium.WebDriver
- Selenium.WebDriver
- Selenium.Support
在安装时,注意看下面的依赖项 版本说明,比如.Appium.WebDriver,需要Selenium 包相关版本为3.8.0+。工具-程序包管理控制台, 输入Get-Package 获取当前项目的NuGet包信息
我安装的版本信息如下,
官网Samples项目版本如下 (Appium官方样例代码下载地址:https://github.com/Microsoft/WinAppDriver/tree/v1.0#using-appium)
Appium版本更新的时候,会优化很多功能,如果报一些错误,可以试试把版本改一改。
https://www.nuget.org/packages 可以搜索关于NuGet包的相关信息
这两个包选择一个使用,我这里选择的- Appium.WebDriver {5.0.0-alpha} NodepadTest
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Service;
using OpenQA.Selenium.Appium.Windows;
using OpenQA.Selenium;
using System;
using System.Threading;
using OpenQA.Selenium.Appium.Interactions;
using OpenQA.Selenium.Interactions;
using System.Collections.Generic;
using OpenQA.Selenium.Support.UI;
public WindowsDriver<WindowsElement> StartNodepadApplication()
{
//1.add capabilities
var appiumOptions = new AppiumOptions();
//appiumOptions.App = @"C:\Windows\System32\notepad.exe";//notepad
appiumOptions.App = "D:\\test\\Appium\\WPFApplication\\AppiumMyApp\\AppiumMyApp\\bin\\x64\\Release\\net6.0-windows\\AppiumMyApp.exe";
appiumOptions.PlatformName = "windows";
appiumOptions.DeviceName = "windowsPC";
//2.start the driver
var appiumLocalServer=new AppiumServiceBuilder().UsingAnyFreePort().Build();
appiumLocalServer.Start();
var driver = new WindowsDriver<WindowsElement>(appiumLocalServer, appiumOptions);
//return the sever session
return driver;
}
[TestMethod]
public void TestWaitForProgressBar()
{
//start session
var session = StartNodepadApplication();
// FindUIElementMethod1 -Name,click clear button
var clearButton = session.FindElementByName("Clear");
clearButton.Click();
// FindUIElementMethod2 -AccessibilityId, click button
var textBox = session.FindElementByAccessibilityId("MyTextBoxID");
textBox.Clear();
//SendKeys
textBox.SendKeys("-----------------------------------------\n");
textBox.SendKeys("******************************************\n");
textBox.SendKeys("my name is jerry\n");
//click save
var saveButton= session.FindElementByName("Save");
saveButton.Click();
//Wait for the progress to process for a while.untill Element appear or disappear
//1.add a DefaultWait,Poll once every 500ms
var wait = new DefaultWait<WindowsDriver<WindowsElement>>(session)
{
Timeout = TimeSpan.FromSeconds(60),
PollingInterval = TimeSpan.FromMilliseconds(500)
};
wait.IgnoreExceptionTypes(typeof(NoSuchElementException));
//界面能找到,说明前面动作已经执行完毕
wait.Until(d => d.FindElementByName("保存(S)"));
//click save button in SaveFileDialog
var dialogSavebutton = session.FindElementByName("保存(S)");
dialogSavebutton.Click();
//2.
var wait2 = new DefaultWait<WindowsDriver<WindowsElement>>(session)
{
Timeout = TimeSpan.FromSeconds(60),
PollingInterval = TimeSpan.FromMilliseconds(500)
};
wait.IgnoreExceptionTypes(typeof(NoSuchElementException));
//find element to verify save done
wait.Until(d =>
{
return d.FindElementByName("文件已导出");
});
session.FindElementByName("确定").Click();
textBox = session.FindElementByAccessibilityId("MyTextBoxID");
textBox.Clear();
textBox.SendKeys("file save done this is second time I am here-\n");
session.Close();
}
项目右键-运行测试-可以看到会开始执行自动化测试。
准备工作:手机开启开发者模式,(一般连续点击系统版本5下),开启usb 调试模式
最好是能使用root 命令的手机,因为我在使用普通手机时,出现了连接不上的问题
第三章提到过,安卓应用启动需要设置以下Capabilities,获取方式标注在了右边
可参考链接:https://blog.csdn.net/u012028250/article/details/120371345
| keyName | 描述 | adb 命令 |
|---|---|---|
| platformName | 手机操作系统 | Android |
| platformVersion | 操作系统版本 | adb shell getprop ro.build.version.release![]() |
| deviceName | 手机或模拟器类型 | adb devices ![]() |
| appPackage | 运行的 Android 应用的包名 | 手机上打开要测试的app,输入 adb shell "dumpsys window | grep mCurrentFocus" ![]() |
| appActivity | 从包中所要启动的 Android acticity | 同上 |
appPackage获取方式有如下几种:package都是com.xxx
adb shell pm list packages列出手机上所有的包,找到所需要的包名adb shell am monitor 获取当前应用程序包名appActivity获取方式:

com.sec.android.app.camera/.Camera t243} ---即appPackage / appActivityadb shell "dumpsys window \| grep mCurrentFocus" 
mCurrentFocus=Window{c22cf8f u0 com.sec.android.app.camera/com.sec.android.app.camera.Camera} ---即appPackage / appActivity
图片中SM-A336B是通过adb shell getprop ro.product.model获取的,指的是手机型号,可填可不填。
appium:automationName:指以什么方式解析界面元素UiAutomator2
填完之后,点击start session,手机会自动安装appium 相关软件,然后就可以启动手机中应用,并且获取界面控件元素了。

private AndroidDriver<AndroidElement> StartApp()
{
System.Environment.SetEnvironmentVariable("ANDROID_HOME", @"C:\\Microsoft\\AndroidSDK\\25");
System.Environment.SetEnvironmentVariable("JAVA_HOME", @"C:\\Program Files\\Microsoft\\jdk-11.0.12.7-hotspot");
//connect to a device or emulator
var capabilities = new AppiumOptions();
capabilities.DeviceName=@"SM-A336B-58f407b9db347ece";
//capabilities.DeviceName = @"58f407b9db347ece";
capabilities.AutomationName = @"UiAutomator2";
capabilities.PlatformName = "Android";
capabilities.PlatformVersion = "12";
//specifying which app we want to install or launch
capabilities.AddAdditionalAppiumOption("appPackage", "com.sec.android.app.camera");
capabilities.AddAdditionalAppiumOption("appActivity", "com.sec.android.app.camera.Camera");
//specify startup flags appium sever to execute adb shell commands
var serveroptions = new OptionCollector();
var relaxedSecurityOption = new KeyValuePair<string, string>("--relaxed-security", "");
serveroptions.AddArguments(relaxedSecurityOption);
var _appiumLocalService = new AppiumServiceBuilder().UsingAnyFreePort().WithArguments(serveroptions).Build();
//start the service
_appiumLocalService.Start();
var driver = new AndroidDriver<AndroidElement>(_appiumLocalService, capabilities);
return driver;
}
[ClassCleanup]//clear up local service
static public void CleanUp()
{
_appiumLocalService?.Dispose();
_appiumLocalService = null;
}
[TestMethod]
public void TestListInstallPackage()
{
AndroidDriver<AndroidElement> driver = StartApp();
//use shell command to list all installed package on the device
string script = "mobile:shell";
var arguments = new Dictionary<string, string>
{
{"command","pm list package" },
{ "----show-versioncode",""},
};
var list = driver.ExecuteScript(script, arguments);
Assert.IsNotNull(list);
Console.Write(list);
}
然后点击运行测试,就可以看到手机相机启动了。
给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru
目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称
@作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors 1、什么是behaviors 2、behaviors的工作方式 3、创建behavior 4、导入并使用behavior 5、behavior中所有可用的节点 6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors 1、什么是behaviorsbehaviors是小程序中,用于实现
遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg
ES一、简介1、ElasticStackES技术栈:ElasticSearch:存数据+搜索;QL;Kibana:Web可视化平台,分析。LogStash:日志收集,Log4j:产生日志;log.info(xxx)。。。。使用场景:metrics:指标监控…2、基本概念Index(索引)动词:保存(插入)名词:类似MySQL数据库,给数据Type(类型)已废弃,以前类似MySQL的表现在用索引对数据分类Document(文档)真正要保存的一个JSON数据{name:"tcx"}二、入门实战{"name":"DESKTOP-1TSVGKG","cluster_name":"elasticsear
我有一个使用SeleniumWebdriver和Nokogiri的Ruby应用程序。我想选择一个类,然后对于那个类对应的每个div,我想根据div的内容执行一个Action。例如,我正在解析以下页面:https://www.google.com/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=puppies这是一个搜索结果页面,我正在寻找描述中包含“Adoption”一词的第一个结果。因此机器人应该寻找带有className:"result"的div,对于每个检查它的.descriptiondiv是否包含单词“adoption
我正在我的Rails项目中安装Grape以构建RESTfulAPI。现在一些端点的操作需要身份验证,而另一些则不需要身份验证。例如,我有users端点,看起来像这样:moduleBackendmoduleV1classUsers现在如您所见,除了password/forget之外的所有操作都需要用户登录/验证。创建一个新的端点也没有意义,比如passwords并且只是删除password/forget从逻辑上讲,这个端点应该与用户资源。问题是Grapebefore过滤器没有像except,only这样的选项,我可以在其中说对某些操作应用过滤器。您通常如何干净利落地处理这种情况?
在我做的一些网络开发中,我有多个操作开始,比如对外部API的GET请求,我希望它们同时开始,因为一个不依赖另一个的结果。我希望事情能够在后台运行。我找到了concurrent-rubylibrary这似乎运作良好。通过将其混合到您创建的类中,该类的方法具有在后台线程上运行的异步版本。这导致我编写如下代码,其中FirstAsyncWorker和SecondAsyncWorker是我编写的类,我在其中混合了Concurrent::Async模块,并编写了一个名为“work”的方法来发送HTTP请求:defindexop1_result=FirstAsyncWorker.new.async.
s=Socket.new(Socket::AF_INET,Socket::SOCK_STREAM,0)s.connect(Socket.pack_sockaddr_in('port','hostname'))ssl=OpenSSL::SSL::SSLSocket.new(s,sslcert)ssl.connect从这里开始,如果ssl连接和底层套接字仍然是ESTABLISHED,或者它是否在默认值7200之后进入CLOSE_WAIT,我想检查一个线程几秒钟甚至更糟的是在实际上不需要.write()或.read()的情况下关闭。是用select()、IO.select()还是其他方法完成
a=[3,4,7,8,3]b=[5,3,6,8,3]假设数组长度相同,是否有办法使用each或其他一些惯用方法从两个数组的每个元素中获取结果?不使用计数器?例如获取每个元素的乘积:[15,12,42,64,9](0..a.count-1).eachdo|i|太丑了...ruby1.9.3 最佳答案 使用Array.zip怎么样?:>>a=[3,4,7,8,3]=>[3,4,7,8,3]>>b=[5,3,6,8,3]=>[5,3,6,8,3]>>c=[]=>[]>>a.zip(b)do|i,j|c[[3,5],[4,3],[7,6],