Aritest 简介
Airtest是一套跨平台的测试框架,它提供了跨平台的API,包括安装应用、模拟输入、断言等。 基于图像识别技术定位UI元素,你无需嵌入任何代码即可进行自动化测试。测试脚本运行后可以自动生成详细的HTML测试报告,让你迅速定位失败的测试点,适用于游戏、App和web,支持平台有Windows、Android、iOS和浏览器。
Airtest 集成的多个测试框架,基于web自动化测试的selenuim框架,基于Chrome Devtools Protocol协议,自动录制生成selenium脚本,精确定位与操作界面元素,支持多浏览器(Chrome、Safari、Firfox等主流浏览器);Poco基于UI控件识别的自动化框架,目前支持Unity3D/cocos2dx-/Android原生app/iOS原生app/微信小程序,也可以在其他引擎中自行接入poco-sdk来使用。
AirtestIDE 是一个强大的GUI工具,是网易配套推出的跨平台UI自动化测试编辑器,它内置了Airtest和Poco的相关插件功能,可以帮助你快速简单地完成录制、编写脚本。
Airtest-selenium测试框架原理图
语言基础(python)
使用Airtest这一套框架,对开发编程语言的能力要求不不高,只要了解python的基本语法,具备基础的python编写脚本的能力即可。
python 特点/特性:
【1】 - 易于学习,入门所需时间极短
【2】- 少量代码即可实现功能,python代码非常精简,只需要少量代码即可构造更多功能
【3】 - 可扩展性非常好,python上有各种各样的模块、工具库,导入即可使用
【4】 - 支持多平台Windows/Linux/macOS,python作为目前美国高校最受欢迎的编程语言之一,可在多个领域平台中广泛使用
【5】- 可与多种编程语言集成(C/Java/C#/ObjectC/Ruby等),且实现稳定性一流。
web测试基础知识
要实现web的自动化测试,需要对web的基础知识和基础概念有基本的了解。
【1】HTML文档的基本构成,HEAD头部包含网页标题,BODY展示网站主体内容,各种类型标签/属性
【2】CSS基本的规则结构,选择器(selector)和声明块(declaration blocks),每个声明则是一个属性-值(property-value)
【3】CSS通配选择器(*universal selector)类选择器(.class selector)ID选择器(#ID selector)类选择器结合元素选择器(element.class selector)多类选择器(.class.class selector)属性选择器(attribute selector)
【4】理解DOM和element之间的关联,DOM并不是JavaScript语言的一部分,它是浏览器的一部分,可以通过JavaScript语言的document对象全局访问,也就是说, html 文件通过浏览器解析,解析之后就形成了一颗 DOM 树,可以通过JavaScript 来动态修改 html 的内容,也就是修改网页的内容和排版。
【5】Selenium提供的常用定位方式,ID,Name,Class,CSS selector和xpath。
完整的测试流程
一个完整的测试流程应该包含哪些?
【1】首先,要有清晰的测试需求,明确要进行测试的对象、范围以及测试目的;测试是一个持续性可重复操作的一个验证过程,在不同的阶段,对于不同的项目系统/产品,存在不同的测试目的及测试手段,测试对象及范围都不一样的。清楚当前将要进行的测试是为了达到什么目的,收到什么样的成果以及需要达到一个什么程度,定下项目的测试范围。
【2】明确了测试对象,测试需求也理清楚了,就可以开始制定测试方案。 选用什么框架、需要什么辅助的技术工具、支持什么平台、需要什么样的测试环境,当这一切方案都定下来,就可以开始着手编写测试用例。
【3】规范的测试用例,包含步骤(STEPS)的详细描述,对应测试的预期结果(EXPECT RESULT),运行该测试的预设条件(PRECONDTION),该测试用例的要用到测试数据(TEST-DATA),很多公司特别是大公司对用例的模板规范都有着严格的要求。 对于自动化测试来说,同样重要。
【4】编写脚本,在代码脚本中适当地注释对于以后维护起着相当重要的作用,特别是如果代码行比较多的情况下。通常会优先选择基础功能验证的自动化,可替代手工式黑盒的日常回归测试。因为并非所有的测试用例都适合做自动化测试,因而一般会选择比较核心的单一功能来实现;而测试脚本的每个用例也是尽量精简,多个精简的用例串起来执行就可以是一个完整的测试流程,尽量避免一个脚本跑太复杂的用例测试,当一个测试脚本包含太多步骤的时候,不仅仅会耗费的时间同样会增加调式的难度,以及日后脚本维护的复杂度。
【5】测试脚本完成,通过调试,测试环境也搭建好了,就可以执行测试脚本,查看测试报告,记录反馈BUG,定时执行测试。
安装与环境搭建
Airtest是基于python语言开发的测试框架,需要先把python环境搭建好,再安装Airtest/AirtestIDE。安装时需要注意的是版本的依赖关系,最新版python没有airtest依赖的库,而且最新版本python没有二进制的只有源码,所以搭建时建议使用的是旧一点2.7版本的python :
• Latest Python 2 Release - Python 2.7.15
搭建python环境
到python官网下载对应系统的压缩包:
https://www.python.org/
1)Window 平台安装Python
下载对应的压缩包版本 :https://www.python.org/downloads/release/python-2715/
64位系统的下载Windows x86-64 MSI installer,32位系统的下载Windows x86 MSIinstaller
python的安装非常简单,下载后打开安装包,按照python的安装向导一直next即可完成,安装时记得要把Add to
path 打勾就不需要自己修改环境变量了 。
2)MAC 平台安装Python
系统是OS X 10.8或者最新的10.9 Mavericks的MAC是自带python2.7的,如果OS版本没有包含,自行去下载即可.
用 dmg / pkg 安装包进行安装,如果有安装 brew 的话就可以通过命令行进行安装。
3)Unix & Linux 平台安装Python
下载适用于Unix/Linux 的源码压缩包(source tarball)
解压压缩包
执行 ./configure 脚本
make
make install
安装完成后,Python 会在 /usr/local/bin 目录下,Python 的库一般安装在/usr/local/lib/pythonXX
设置环境变量,export
PATH="$PATH:/usr/local/bin/python"
下载并安装Airtest/AirtestIDE
执行以下命令进行安装:
pip install airtest
需要注意,当时安装Airtest的时候遭遇失败,原因是最新版本的python没有airtest依赖的库,在最新版本python没有二进制的只有源码,所以干脆就重新安装一个旧的2.7版本的python了。
安装成功!
官网上下载对应系统的最新版IDE:http://airtest.netease.com/changelog.html
下载后解压即可用
IDE使用指南
1 - 启动IDE
2 - 录制脚本
3 - 编写脚本
4 - 运行脚本
5 - 查看测试报告
启动
进入已解压的AirtestIDE目录,打开AirtestIDE的应用程序,IDE会运行一个后台服务以及一个IDE应用界面窗口
IDE启动后默认的界面布局如下图:
录制web测试脚本
AirtestIDE 默认没有不会打开web录制的窗口,需要手动去打开:
打开web录制窗口后,第一次运行需要修改一下IDE对浏览器的设置,打开选项 - 设置(setting),配置selenium打开浏览器应用程序的路径
快速操作按键打开一个新脚本文件,通过selenium window打开一个新的录制窗口:
Do you want to insert poco init code at the current curosr position [Yes] [No] 是否导入初始化代码到光标所在位置,选择【Yes】之后会导入selenium相关的依赖库,只在第一次需要导入。
通过selenium window - start_web 打开一个新页面,输入需要进行测试的URL地址:
在刚打开录制的浏览器窗口,输入测试URL地址,点击selenium window - 录制按钮,即可开始录制测试脚本:
selenium 窗口还提供了一些常用的录制操作,如下图所示:
编写脚本
脚本编辑窗口右上角有一个小菜单,可以快速打开该脚本的报告目录、报告、打开最后运行的日志报告和清理运行截图
Web 录制无法模拟的按键操作,可以手动通过代码来完成,比如send keys, keys.ENTER 等等。
常见的操作元素方法如下:
- clear 清除元素的内容
- send_keys 模拟按键输入
- click 点击元素
- submit 提交表单send_keys(Keys.RETURN)相当于回车登录, 如果需要输入中文,防止编码错误使用send_keys(u"中文用户名")
通过WebElement接口可以获取常用的值:
- size 获取元素的尺寸
- text 获取元素的文本
- get_attribute(name) 获取属性值
- location 获取元素坐标,先找到要获取的元素,再调用该方法
- page_source 返回页面源码
- driver.title 返回页面标题
- current_url 获取当前页面的URL
- is_displayed() 设置该元素是否可见
- is_enabled() 判断元素是否被使用
- is_selected() 判断元素是否被选中
- tag_name 返回元素的tagName
常见键盘操作
- send_keys(Keys.ENTER) 按下回车键
- send_keys(Keys.TAB) 按下Tab制表键
- send_keys(Keys.SPACE) 按下空格键space
- send_keys(Kyes.ESCAPE) 按下回退键Esc
- send_keys(Keys.BACK_SPACE) 按下删除键BackSpace
- send_keys(Keys.SHIFT) 按下shift键
- send_keys(Keys.CONTROL) 按下Ctrl键
- send_keys(Keys.ARROW_DOWN) 按下鼠标光标向下按键
- send_keys(Keys.CONTROL,'a') 组合键全选Ctrl+A
- send_keys(Keys.CONTROL,'c') 组合键复制Ctrl+C
- send_keys(Keys.CONTROL,'x') 组合键剪切Ctrl+X
- send_keys(Keys.CONTROL,'v') 组合键粘贴Ctrl+V
运行脚本
执行菜单栏下的快捷操作按钮运行脚本:
脚本运行时,log查看窗口会实时打印代码运行日志:
包含脚本启动信息,语句执行情况,截图位置,运行结果及耗费时间。
查看测试报告
执行菜单栏下的快捷操作按钮查看测试报告:
测试报告包含测试用例目录名称,作者,运行时间,测试点汇总,每个步骤的执行时间、测试内容、截图及运行结果
AirtestIDE官方说明文档
https://airtest.netease.com/docs/docs_AirtestIDE-zh_CN/index.html
查找定位元素
AirtestIDE 对web录制支持已经挺完善的了,大部分的控件操作都可以通过录制来完成,最简单的使用方法就是录制脚本,但其实不仅仅是录制,有时候通过查找定位元素来编辑脚本会更加加强测试脚本的使用性,毕竟IDE是通过界面录制进行的,有的界面元素通过录制可能会取不到,又或者包含动态的一些无法直接完成操作测试的实际情况,所以更简单有效的办法就是录制+编写。
find element
selenium.webdriver 模块提供了所有WebDriver的实现, 当前支持的WebDriver有: Firefox, Chrome, IE and Remote。 `Keys`类提供键盘按键的支持,大量的方法让你去查询页面中的元素:
较常用查找定位元素使用的方法通过id、name、xpath、selector、classname,接下来会逐一介绍这几种find element的使用方法。
find element by id:
确定元素的id名称,直接通过id查找可定位到元素,将返回页面查找到的第一个id元素
find_element_by_id("loginsubmit")
find element by name:
与id同样,当确定元素的name名称时,可直接通过name查找定位元素,同样地,selenium将会返回页面查找到的第一个name元素
find_element_by_name('username')
find_element_by_name("password")
find element by xpath:
xpath在xml文档中可遍历节点元素和属性,是XML路径语言。xpath同时扩展了通过id/name查找元素的方式,即是说,如果当前元素是id值,xpath会自动识别成("//*[@id="top-menu"]),同时诸多类型如input、button、锚点a、iamge等都可以进行判断,比如 ("//button[@data-role='submit']") ,xpath的形式复杂多变,对于网站页面千变万化的内容而言,xpath能够很好的自适应。
使用相对路径的写法以双斜线开头,绝对路径以单斜线开头从html最顶层开始遍历,如下图:
find_element_by_xpath("/html/body/form[1]") #绝对路径
find_element_by_xpath("//form[1]") #页面中的第一个form元素
find_element_by_xpath("//form[@id='loginForm']") #包含id属性并且其值为loginForm
一般格式为//tagname[@attribute='value']/路径/路径,如//[@id="J_PmTaskInput"]/div/label , [*]表示包含所有
但不推荐使用绝对路径,因为页面可能经常元素变化,稍有改动可能就会导致定位失败;相对路径定位相对而言位置关系改动的概率更低,更靠谱,这样脚本健壮性适应性更强。
有一些非常有用的插件,可以协助发现元素的XPath:
- XPath Checker - suggests XPath and can be used to test XPath results.
- Firebug - XPath suggestions are just one of the many powerful features of this very useful add-on.
- XPath Helper - for Google Chrome
通过就近的包含id或者name属性的元素出发定位你的元素,这样相对关系就很靠谱, 因为这种位置关系很少改变,所以可以使你的测试更加强大。
find elment by css selector:
通过css选择器定位元素,将返回页面第一个匹配到的元素。
find_element_by_css_selector("#J_Milestone > div.os-milestone-check > div")
find_element_by_css_selector("button.ui.teal.J_Pay") #查找tagtype.class.class..
find_element_by_css_selector("div.ui.negative.button") #查找所有div标签里面class包含ui、negative、button的元素
常用的选择器:
#表示id,查找id名称为J_Milestone的属性,以及该属性的下一级div
.表示class,查找该id下一级的div的class为os-milestone-check
*表示所有元素,>表示下一级
[]表示attribute,属性名称,[attribute=value] 指某属性值的该属性
:nth-child(n) 表示该父元素的第几个子元素
css selector 相较于xpath的优点就是性能比xpath好,另外就是页面排版布局位置有时候会变,但css selector相对更稳定,名称一般不会经常变更。
find element by class name:
确定元素的class name,可通过class name直接查找定位元素,将会返回该页面第一个匹配的class属性的元素。
find_element_by_class_name('content')
当页面存在多个相同class属性值的元素时,通过class name查找也只能返回第一个结果。
find element by link text:
这是一个查找超链接非常方便的一个定位方法,当确切知道页面的某个超链接使用的标签文本名称,那可以直接通过find_element_by_link_text 查找,将会返回页面第一个匹配的锚点标签。
find_element_by_partial_link_text('快速入门')
find element by partial link text:
这也是一个查找超链接的方法,跟find_element_by_link_text的区别在于,前者是精确查找,后者是模糊查找,也是相当好用的定位方法;同样地,也是会返回页面第一个匹配的锚点标签。
find_element_by_partial_link_text('入门')
以上查找元素find elmenet的方法,不管是哪一种,都是只会返回页面第一个匹配的元素的;如果没有查找到任何匹配的元素,将会返回一个异常:
返回多个元素
在实际运用中,还会存在一种情况,就是查找元素的时候,当前页面返回的元素匹配的结果不止一个,find elment只能返回第一个结果,而有些情况下某些被需要的测试元素恰恰并不是第一个返回要的结果。
举一个实际项目中的遇到的例子,某网站的某个页面上一个时间控件,测试需求需要对时间控件进行选择操作,页面内容如图:
直接使用find element来定位元素,会返回一个错误的结果:
find_element_by_xpath("//button[@class_name='xdsoft_next']").click()
selenium.common.exceptions.ElementNotVisibleException:Message:elementnotvisible
原因是该页面上有2个时间控件,所以返回的操作按键结果也不止一个,而find_element返回的第一个结果恰巧在该页面该操作下是不被需要的,是被隐藏的元素,所以对其进行操作时会报错 element is not visible ,一个解决办法就是通过find elemets来查找多个元素。
find elements 查找元素的方法跟find element 是类似的,不同的是find elements会返回一个list列表。
- find_elements_by_name
- find_elements_by_xpath
- find_elements_by_link_text
- find_elements_by_partial_link_text
- find_elements_by_tag_name
- find_elements_by_class_name
- find_elements_by_css_selector
上面的示例中,通过下面的代码可以定位到下个月按钮的元素,并对其进行操作:
find_element_by_xpath("//div[@class='xdsoft_mounthpicker']/button[2]") #通过xpath定位到指定的button
find_elements_by_class_name('xdsoft_next')[2] #通过classname定位到指定的button
在xpath过滤后还可以进入下一级过滤的,可以用 /button[n] 取下一级的某个索引,索引的用法只适用于直接在某种类型的element后面,比如:
find_element_by_xpath("//div[@class='xdsoft_mounthpicker']/button[1]")
初学者容易会写成下面的错误示范:
find_element_by_xpath("//button[@class='xdsoft_next'][1]") #属性过滤后无法直接调用索引
通过以上方法就可以成功对该元素时间控件下个月按钮进行点击操作了。
断言
一个完整的测试脚本一定会包含对测试结果是否与预测结果一致的对比,从而判断脚本是否执行成功,Airtest提供了以下断言方法:
assert_exists
assert_not_exists
assert_equal
assert_not_equal
assert exists 使用方法:
assert_exist("//*[@id=\"main\"]/div/div[2]/h3", "xpath", "请填写测试点.") #判断该元素在页面是否存在
如果元素不存在,则会抛出一个异常,AssertionError("%s does not exist in screen, message: %s" % (v, msg)), 在log查看窗口可看到实时的调试日志。
assert_not_exists 使用方法:
assert_not_exist("//*[@id=\"main\"]/div/div[2]/h3", "xpath", "请填写测试点.") #判断该元素在页面是否不存在
如果返回结果不匹配,则会抛出异常,AssertionError("%s exists unexpectedly at pos: %s, message: %s" % (v, pos, msg)) ,在log查看窗口可看到实时的调试日志。
assert_equal 使用方法:
assert_equal(driver.find_element_by_id("top-menu").text, "确认完成", "发布确认完成")#判断id属性为top-menu的元素文本信息是否等于“确认完成”
如果元素结果不匹配,则会抛出异常,AssertionError("%s and %s are not equal, message: %s" % (first, second, msg)),在log查看窗口可看到实时的调试日志。,在log查看窗口可看到实时的调试日志。
assert_not_equal 使用方法:
assert_not_equal(driver.find_element_by_id("J_FinishProject").text, "完成", "流程状态为已完成")#判断id属性为"J_FinishProject的元素文本信息是否等于“完成”
与assert equal类似,如果返回结果不匹配的就会抛出异常。
Airtest 提供的断言种类并不算丰富,只能简单地对某个测试结果或状态进行判断,实际项目种更为常用的应该是assert equal了,而且如果使用自己构建的函数对测试结果进行判断,那么该代码执行的测试结果并不会记录在html的测试报告里面。为了让assert断言可以完成更多的事情,可以使用传入函数的方法来进行判断,比如下面的例子:
def check_finish(): pro_status = driver.find_element_by_css_selector("div.panel.newbie-container.J_ProjectCommit > div.ui.form.fluid > div > div > div:nth-child(1) > div.content > div.extra.text").text
status = "指派成功" if status in pro_status: print("project finishs!") return True else : print("project not finish, please check!") return Falseassert_equal(check_finish(), True, "Project all finish! AutoTest is done!")def pro_assign(dev_id): driver.find_element_by_xpath("//input[@placeholder='用户UID']").send_keys(dev_id)
driver.find_element_by_id("prostatus_appoint_submit").click() driver.switch_to.alert.accept() def check_devstatus(): pro_devstatus = driver.find_element_by_css_selector("div.panel.newbie-container.J_ProjectCommit > div.ui.form.fluid > div > div > div:nth-child(1) > div.content > div.extra.text").text
pro_devassign = "对接了第1位开发者,等待开发者确认" if pro_devassign in pro_devstatus:
return True else : return False print("后台指派第一位开发者异常,请手动查看项目状态") driver.quit() assert_equal(check_devstatus(), True, "指派成功!")
这样依然可以在html测试报告显示函数执行后的结果,而且使用更灵活:
结束语
总体来说,作为一个年龄还算比较新的测试框架,这个测试工具还有许多不足的地方,需要完善的地方,比如启动浏览器时间比较久,需要等待浏览器渲染页面脚本加载完成才有办法执行步骤,不能从脚本中途进行断点调试,有时候打开多个窗口的时候录制窗口会失焦等,但个人认为这仍是一个非常好用的界面自动化测试的入门工具。而且自今为止,AirtestIDE还有在持续地更新版本,在App的自动化测试上使用的更广泛,希望可以越来越完善,应用更广泛。
简书的编辑器真的是弱到,文章写好发布都好费劲