Web UI 自动化工具选型调研(seldom)
1.背景
目前公司业务比较稳定,但是日常更新需求需要大量的重复的回归的测试,占用部分时间。为了提高效率可使用UI自动化来减少一部分人员手工执行投入以提高效率,也可以作为日常线上环境主流程功能的回归。
2. 什么类型的项目适合自动化
- 需求不会频繁变动:因为需求频繁变动,页面的功能就会频繁变动
- UI比较稳定:因为UI自动化就是基于UI
- 项目周期较长
- 大量的回归测试任务:大量的重复的回归的测试任务,不断的迭代,需要回归老功能。
- 冒烟测试:针对本次迭代的新功能(核心的、主干的功能,大概10%~20%)进行冒烟测试。
- 回归测试:对老功能进行回归测试
2. 自动化测试场景
场景列如:
- 打开网站(比如:打开淘宝网站)
- 定位元素(比如:定位到搜索输入框)
- 操作元素(比如:在搜索框中输入秋装,点击搜索)
- 模拟页面动作(比如:下拉、上滑等)
- 断言结果:预期结果与实际结果比对,判断是否通过测试。
- 生成报告
4.UI自动化工具选择
4.1 Seldom VS Macaca
名称 |
seldom 基于 unittest 单元测试框架开发 |
Macaca |
许可 |
开源工具 |
开源工具 |
客户支持 |
社区论坛 QQ群 |
官方不再维护 |
测试支持 |
支持基于Web和桌面的应用程序的测试 及接口api测试 |
支持基于Web和桌面的应用程序的测试。 |
支持的编程语言 |
Python |
Node.js/Java/Python |
支持的环境 |
Windows, Linux, Mac |
Windows, Linux, Mac |
支持的浏览器 |
谷歌浏览器,Mozilla Firefox,Internet Explorer,Edge,Opera,Safari等 |
谷歌浏览器,Mozilla Firefox,Internet Explorer,Edge |
测试报告生成 |
在工具中生成内置测试报告 |
在工具中生成内置测试报告。 |
持续集成 |
提供集成方案 |
提供了多种集成方案,便于测试及接入 |
对比结果:
seldom相比较Macaca,seldom支持web/app/api 测试,是一个全功能的测试框架,而且提供脚手架快速创建自动化项目,也集成了测试报告及报告发送的多种方式,最主要有群提供技术支持,便于问题解决。由于Macaca目前用户较少官方不再维护,社区已不再活跃,故目前web端测试方案选择seldom。
4.2 seldom框架
4.2.1.介绍
seldom 基于 unittest 单元测试框架开发,并集成了web/app/api测试库,可进行web UI及APP UI自动化测试以及接口测试
seldom特点
- 支持测试类型(web/app/api)
- 丰富的断言
- 生成随机测试数据
- 用例依赖
- 用例分类标签
- 支持发送(邮件、钉钉、飞书、企微)消息等
- 日志打印
- 缓存cache
- 命令行工具
- 强大的数据驱动(JSON/YAML/CSV/EXCEL)
- HTML/XML报告
- 失败重跑&截图
- 数据库操作(MySQL/sqlite3/Mongodb)
- 支持平台化
4.2.2 安装
pip install seldom
随时体验最新的代码,可以使用下面的命令
pip install -U git+https://github.com/defnngj/seldom.git@master
4.2.3 项目创建
seldom -P mypro
目录结构:
mypro/ ├── test_dir/ │ ├── __init__.py │ ├── test_web_sample.py │ ├── test_api_sample.py ├── test_data/ │ ├── data.json ├── reports/ └── confrun.py
test_dir/
测试用例目录。test_data/
测试数据文件目录。reports/
测试报告目录。confrun.py
运行测试用例配置文件
4.2.4 用例创建
import seldom class YouTest(seldom.TestCase): def test_case(self): """a simple test case """ ... if __name__ == '__main__': seldom.main() |
根据自己的需求编写Web UI
or HTTP接口
自动化测试
用例编写规则 :
- 一个脚本是一个完整的场景,从用户登陆操作到用户退出系统关闭浏览器
- 一个脚本脚本只验证一个功能点,不要试图用户登陆后把所有的功能都进行验证再退出系统
- 尽量只做功能中正向逻辑的验证,不要考虑太多逆向逻辑的验证,逆向逻辑的情况很多
- 脚本之间尽量不要产生关联性
- 用例从手工测试当中选取,尽量选择简单且需要反复回归,稳定且不会经常变化,优先覆盖核心功能
4.2.5 运行测试
seldom 的运行有三种方式:
main()
方法:在.py
文件中使用seldom.main()
方法。seldom
命令:通过sedom
命令指定要运行的目录&文件&类&方法。pycharm
右键执行:这种方式无法读取到配置,有严重缺陷。
a. main()
方法运行测试
创建 test_sample.py
文件,在测试文件中使用main()
方法,如下:
# test_sample.py import seldom from seldom import data class YouTest(seldom.TestCase): def test_case(self): """a simple test case """ self.assertEqual(1+1, 2) @data([ ("case1", "seldom"), ("case2", "XTestRunner"), ]) def test_ddt(self, name, search): """ ddt case """ print(f"name: {name}, search_key: {search}") if __name__ == '__main__': # 指定运行其他目录&文件 seldom.main(path="./") # 指定当前文件所在目录下面的用例。 seldom.main(path="./test_dir/") # 指定当前目录下面的test_dir/ 目录下面的用例。 seldom.main(path="./test_dir/test_sample.py") # 指定测试文件中的用例。 seldom.main(path="D:/seldom_sample/test_dir/test_sample.py") # 指定文件的绝对路径。 # 运行当前文件中的用例 seldom.main() # 默认运行当前文件中所有用例 seldom.main(case="test_sample") # 指定当前文件 seldom.main(case="test_sample.TestCase") # 指定测试类 seldom.main(case="test_sample.TestCase.test_case") # 指定测试用例 # 使用参数化的用例 seldom.main(case="test_sample.TestCase.test_ddt") # 错误用法 seldom.main(case="test_sample.TestCase.test_ddt_0_case1") # 正确用例 |
b.seldom命令执行
> cd mypro/ # 进入项目根目录 > seldom -p test_dir # 运行目录 > seldom -p test_dir/test_sample.py # 运行文件 > seldom -m test_dir.test_sample # 运行文件 > seldom -m test_dir.test_sample.SampleTest # 运行 SampleTest 测试类 > seldom -m test_dir.test_sample.SampleTest.test_case # 运行 test_case 测试方法 |
4.2.6 测试报告
seldom 默认生成HTML测试报告,在运行测试文件下自动创建reports
目录
定义测试报告
seldom.main(path='testadminLoginTest.py', title="运营后台登录页面UI测试报告", description="admin登录", report="adminlogintest.html", tester="xxx", language='zh-CN' ) |
- report: 配置报告名称和路径。
- title: 自定义报告的标题。
- tester: 指定自动化测试工程师名字。
- description: 添加报告信息
接入钉钉机器人
- 钉钉uitest群机器人:
- 签名:xxxx
- access_token:xxxx
import seldom from seldom import DingTalk # ... if __name__ == '__main__': seldom.main() ding = DingTalk( access_token="xxxx", key="", app_secret="xxxxx", at_mobiles=[13700000000, 13800000000], is_at_all=False, ) ding.sender() |
参数说明:
access_token
: 钉钉机器人的 access_tokenkey
: 如果钉钉机器人安全设置了关键字,则需要传入对应的关键字。app_secret
: 如果钉钉机器人安全设置了签名,则需要传入对应的密钥。at_mobiles
: 发送通知钉钉中要@人的手机号列表,如:[137xxx, 188xxx]。is_at_all
: 是否@所有人,默认为 False, 设为 True 则会@所有人
发送邮件
import seldom from seldom import SMTP # ... if __name__ == '__main__': report_path = "/you/path/report.html" seldom.main(report=report_path) smtp = SMTP(user="send@126.com", password="abc123", host="smtp.126.com") smtp.sendmail(to="receive@mail.com", subject="Email title", attachments=report_path, delete=False) |
subject
: 邮件标题,默认:Seldom Test Report
。to
: 添加收件人,支持多个收件人:["aa@mail.com", "bb@mail.com"]
。attachments
: 设置附件,默认发送 HTML 测试报告。delete
: 是否删除报告&日志。(在服务器上运行自动化,每次都会产生一份报告和日志,手动删除比较麻烦
4.2.7 元素定位
seldom 提供了8中定位方式,与Selenium保持一致。
- id_
- name
- class_name
- tag
- link_text
- partial_link_text
- css
- xpath
self.type(id_="kw", text="seldom") self.type(name="wd", text="seldom") self.type(class_name="s_ipt", text="seldom") self.type(tag="input", text="seldom") self.type(xpath="//input[@id='kw']", text="seldom") self.type(css="#kw", text="seldom") self.click(link_text="hao123") self.click(partial_link_text="hao") |
使用优先级:
ID>Name>CSS>XPath
优先级最高:id ,但id有时是变的,如果id是不变的,可用它来定位
优先级其次:name
优先级更次:css
优先级更次:xpath
使用注意点 :
1. 如果 HTML 的 id 是可用的、唯一的,那么它就是在页面上定位元素的首选方法
2. 如果没有唯一的 id或name,那则使用XPath 和 CSS 定位, XPath 选择器运行速度较慢
3.尽量让开发把关键元素加上id或name属性,减少不合理的页面元素
4.2.8 断言
# 断言标题是否等于"title" self.assertTitle("title") # 断言标题是否包含"title" self.assertInTitle("title") # 断言URL是否等于 self.assertUrl("url") # 断言URL是否包含 self.assertInUrl("url") # 断言页面包含“text” self.assertText("text") # 断言页面不包含“text” self.assertNotText("text") # 断言警告是否存在"text" 提示信息 self.assertAlertText("text") # 断言元素是否存在 self.assertElement(css="#kw") # 断言元素是否不存在 self.assertNotElement(css="#kwasdfasdfa") |
4.2.9 Page Objects设计模式
seldom API 的设计理念是将元素操作和元素定位放到一起写的,需要结合poium 一起使用 ,
> pip install poium==1.1.6
#!/usr/bin/env python # -*- coding: utf-8 -*- # author: HuJun # datetime: 2023/3/3 16:55 # -*-coding:utf-8-*- # 用于登录页元素定位 from models import Url from poium import Page, Element class login(Page): """ 项目用户登录、退出定位元素""" '用户名输入框' username_input_loc = Element(class_name="el-input__inner") '密码输入框' password_inpput_loc = Element(xpath="/html/body/section/div/div[2]/form/div[4]/div/div/input") '登录按钮' loginButonClick_loc = Element(class_name="action-group") '验证码输入框' verificationCode_loc = Element(xpath="/html/body/section/div/div[2]/form/div[7]/div/div[1]/input") '退出系统按钮' # 操作方法封装 def username_input(self,key): self.username_input_loc.clear() self.username_input_loc.send_keys(key) def passWord_input(self,key): self.password_inpput_loc.clear() self.password_inpput_loc.send_keys(key) def loginButon_click(self): self.loginButonClick_loc.click() def verificationCode_input(self,key): self.verificationCode_loc.send_keys(key) def login_all(self): self.username_input_loc.clear() self.username_input_loc.send_keys("admin") self.password_inpput_loc.clear() self.password_inpput_loc.send_keys("ngmmsupermanager") self.loginButonClick_loc.click() |
其他用法可去官网查看:https://seldomqa.github.io/