测试自动化是软件行业一个不可逆转的趋势,目前大多数自动化测试采用录制回放或直接调用selenium/appnium接口来编写测试代码的方式实现,主要存在以下不足:
- 代码即用例,用例越多,代码量越大,维护越困难;
- 受UI变化影响大,测试对象的页面结构变化,需要修改对应的代码;
- 对编码能力要求高;
为了解决以上问题,sweetest应运而生,有以下特点:
- 用例与代码隔离、元素定位与代码隔离;
- 在 Excel 中以文本编写测试用例;
- 元素定位表格化,采用“变量定位法”;
- 使用简单,没有编码基础也能快速上手;
- 开发语言:python3
- 底层工具:selenium + unittest
- 用例工具:excel
- 报告模式:excel、html
- 日志模式:log
- 错误截图:png
- 消息通知:钉钉群消息、邮件
- 快速使用:
- 在element/XX-Elements.xlsx文件中书写测试页面中各测试元素的定位信息;
- 在testcase/XX-TestCase.xlsx文件中书写测试用例;
- 若有全局变量,可保存在data/XX-YY.csv文件中;
- 可在sweetest/config.py文件中修改邮件及钉钉相关配置信息;
- 修改sweetest/globals.py文件中的g.project_name(项目名称)、g.sheet_name(本次需执行的测试用例集)、g.conditions(用例筛选条件)及环境信息(平台、浏览器);
- 运行start.py启动本次自动化测试;
目录 | 说明 |
data xx.csv |
测试数据,文件名称格式为:project_name-sheet_name.csv 每个testsuite对应一份数据文件 |
element xx.xlsx |
元素定位表,文件名称格式为:project_name-Elements.xlsx |
testcase xx.xlsx |
测试用例,文件名称格式为:project_name-Testcase.xlsx excel文件中每个sheet相当于testsuite |
template Template.html |
Html格式测试报告的模板 |
report xx.xlsx |
Excel格式测试报告,每次自动化测试结束后自动生成一份,名称格式为:project_name-Report@yyyymmdd_HHMMSS.xlsx report目录可以不存在,程序运行后会自动创建 |
htmlreport xx.html |
Html格式测试报告,每次自动化测试结束后自动生成一份,名称格式为:project_name-Report@yyyymmdd_HHMMSS.html htmlreport目录可以不存在,程序运行后会自动创建 |
log xx.log |
运行日志,每次自动化测试会自动生成一份,名称格式为:yyyymmdd_HHMMSS.log log目录可以不存在,程序运行后会自动创建 |
snapshot xx.png |
错误截图图片,文件名称格式为:project_name-sheet_name-yyyymmdd_HHMMSS#testcase_title-testcase_step.png snapshot目录可以不存在,程序运行后会自动创建 |
requirements.txt | 依赖包清单 |
start.py | 启动文件 |
sweetest | 主程序代码 |
keywords | 关键字 |
lib | 辅助模块 |
config.py | 配置文件 配置关键字、文件名后缀、元素/用例属性的中英文对照、元素超时时间、页面刷新超时时间、钉钉及邮件相关信息等 |
globals.py | 全局变量文件 定义浏览器对象、测试项目名称、要执行的测试用例集名称、环境配置信息、用例筛选条件等 |
autotest.py | 测试类 |
在excel中书写元素定位信息,将元素定位与代码分隔开,方便维护,可读性高。
字段 | 说明 |
page/页面 | 必填字段 元素所在页面 所有页面通用的元素(如title等 )可放于页面“通用”下面 |
element/元素 | 必填字段 需定位进行操作的元素,不同page下的元素可同名 |
by/定位方式 | 必填字段 定位方式,常用的有id、name、xpath等 |
value/定位值 | 必填字段 定位值 |
custom/自定义 | 选填字段 若元素在frame/iframe中,则填写相应的frame id或frame name |
remark/备注 | 选填字段 注释说明 |
变量定位法:
- 元素定位表中element最后带#号,value的值中带#号
- 测试用例中element带上具体变量值
- 代码在进行元素定位时,自动把变量值填充到定位值相应的位置中
举例:
元素定位表设计:
page | element | by | value |
百度搜索页面 | 搜索结果# | xpath | //*[@id="#"]/h3/a |
测试用例设计:
keyword | page | element |
点击 | 百度搜索页面 | 搜索结果#1 |
则最终代码进行元素定位时,使用的是:find_element_by_xpath('//*[@id="1"]/h3/a')
在excel中书写测试用例,将测试用例与代码分隔开,方便维护,可读性高。
excel文件中每个表单表示1个测试用例集,表单名sheet_name是测试用例集名称(test_suite); 执行哪些测试用例集,需要在globals.py中指定g.sheet_name;
- sheet_name是str字符串,支持多种匹配方式
- ^放开头,表示以此值开头;
- $放结尾,表示以此值结尾;
- *放开头或结尾,表示模糊匹配;
- 非^ *开头,非$ *结尾,表示精确匹配;
- sheet_name是list列表,则excel表单名在列表中的用例都需要执行;
字段 | 说明 |
id/用例编号 | 必填字段 用例/用例片段的id |
title/用例标题 | 必填字段 用例/用例片段的名称 |
condition/前置条件 | 选填字段 取值:base、end、setup、teardown、snippet |
step/测试步骤 | 必填字段 测试执行步骤的编号 所编号前可用逻辑控制符号,取值:^、>、< |
keyword/操作 | 必填字段 测试操作/关键字 |
page/页面 | 选填字段 测试元素所在页面 为空时,自动沿用上个步骤的page值 |
element/元素 | 必填字段 需进行操作的元素,不同page下的元素可同名 |
data/测试数据 | 选填字段 测试数据,用键值对表示 多个data用 英文逗号 或 双英文逗号 隔开 |
expected/预期结果 | 选填字段 预期结果,用键值对表示 为空时,则data列充当expected的内容 |
output/输出数据 | 选填字段 输出数据,用键值对表示 |
priority/优先级 | 选填字段 测试用例的优先级,取值:H/高、M/中、L/低 |
designer/设计者 | 选填字段 测试用例设计者名称 |
flag/自动化标记 | 选填字段 标记是否是自动化测试用例 值为Y,表示是自动化测试用例 值为N,表示不是自动化测试用例 值为空,默认为自动化测试用例 |
score/步骤结果 | 选填字段 测试步骤的执行结果记录 |
result/用例结果 | 选填字段 测试用例的执行结果记录 |
remark/备注 | 选填字段 注释说明 |
-
前置条件 / condition
- base:相当于setUpClass,每个用例集前执行一次;
- end:相当于tearDownClass,每个用例集后执行一次;
- setup:相当于setUp,每个用例前执行一次;
- teardown:相当于tearDown,每个用例后执行一次;
- snippet:共用的用例片段,可供调用;
-
测试步骤 / step
- ^:相当于if语句;
- <:相当于else语句;
- >:相当于then语句;
-
测试数据 / data
-
可为空;
-
data中以=分隔键值对;
-
多个data用 英文逗号 或 双英文逗号 隔开;
-
若data中本身带有英文逗号,需转义;
-
若data中没有=号,直接赋值给text;
-
data中可带变量,用<>括起部分为变量;
- data中变量替换时默认取全局变量的第一个值;
- 若想保留原全局变量的数据结构(比如,全局变量 var是list,想替换变量的值也为list),则在data中设置: 数据结构=是、datatype=y、datatype=yes;
-
data中直接带+、-、*、/、%、(、)、<、>,即运算符 和 变量定界符,需转义;
-
data中+、-、*、/、%、(、)左边或右边字符 非数字,也可不转义;
-
data中可以带按键操作,如:text=<Keys.ENTER>、<Keys.CONTROL,'a'>;
-
data中可带运算表达式,代码自动执行计算;
-
data中可带list,支持或模糊匹配;
举例说明:
- key=['v1','v2','v3'],则检查key=v1 或 key=v2或 key=v3都表示用例通过;
- key=['^v','v$','*a','v4'],则检查key 以v开头 或 以v结尾 或 包含a 或 等于v4 都表示用例通过;
-
data中设置等待时间=x,可强制先等待x秒再执行操作;
-
-
预期结果 / expected
- 测试数据(data)为空时,可以将预期结果(expected)放在测试数据(data)列
- 满足测试数据(data)的一切规则
- 支持多种匹配方式
- ^放开头,表示以此值开头
- $放结尾,表示以此值结尾
- *放开头或结尾,表示模糊匹配
- 非^ *开头,非$ *结尾,表示精确匹配
-
输出数据 / output
- 以=分隔键值对,key是自定义的变量,value是具体值;
- 若value为text,则取element的text值赋值给变量key;
- value中可带变量,用<>括起部分为变量;
- value中变量替换时默认取全局变量的第一个值;
- 若想保留原全局变量的数据结构(比如,全局变量 var是list,想替换变量的值也为list),则在value中设置: 数据结构=是、datatype=y、datatype=yes;
- 若value中带变量,则将替换变量后的value值赋值给变量key;
- 若value中不带变量、且不为text,则取element对应的属性值赋值给变量key;
-
元素 / element
- 多个element用 | 隔开,比如拖拽操作涉及2元素;
- element中可带变量,用<>括起部分为变量;
- element中带#号,表示写用例时,后面需带元素定位值;
- element可为用例片段ID(用例片段ID以"SNIPPET"开头);
keyword | page | element | data |
打开 | 通用 | 百度搜索链接 | 标签页名=百度搜索页面,,清理缓存=是,,打开方式=新标签页 |
注意:
- open /打开 关键字对应的元素可以为 浏览器 或 browser,表示打开浏览器;
- open /打开 关键字对应的其它元素 必须定义在“通用”page下;
- 可以设置打开的页面名称:标签页名=xxx、新窗口=xxx、tabname=xxx,该新页面的元素则定义在该page下;
- 可以设置清缓存:清理缓存=是、cookie=yes;
- 可以设置在浏览器中新开tab打开链接:打开方式=新标签页、mode=tab;
- 可以设置新开浏览器窗口打开链接:打开方式=新浏览器、mode=browser;
keyword | page | element | data |
检查 | 百度搜索页面 | 页面title | 百度一下,你就知道 |
keyword | page | element | data |
检查 | 首页 | 欢迎文案 | text=欢迎 |
注意:
- check / 检查 关键字用于配合做结果断言检查;
- 常用于检查跳转页面title是否正确、某元素的text属性是否正确等 ;
- 若用例中data列有值,则取data列的值做检查;否则取expected列的值做检查;
- 检查时可以使用多种匹配方式:
- ^放开头,表示以此值开头;
- $放结尾,表示以此值结尾;
- *放开头或结尾,表示模糊匹配;
- 非^ *开头,非$ *结尾,表示精确匹配;
- 若用例中data/expected列是列表,则支持“或”模糊匹配,举例如下:
- key=['v1','v2','v3'],则检查key=v1 或 key=v2或 key=v3都表示用例通过;
- key=['^v','v$','*a','v4'],则检查key 以v开头 或 以v结尾 或 包含a 或 等于v4 都表示用例通过;
keyword | page | element | data |
输入 | 登录页 | 账号 | tester |
注意:
- 代码默认先清空文本再输入对应内容;
- 可以设置不清空文本:清除文本=否、clear=no
- 可以输入文本:tester
- 也可以进行按键操作:<Keys.ENTER>、<Keys.CONTROL,'a'>
keyword | page | element | data |
点击 | 登录页 | 登录按钮 |
注意:
- click / 点击 关键字,通常用于操作按钮或链接文字;
- 点击操作若会打开新页面/新窗口,可以设置新页面/新窗口的名称:标签页名=xxx、新窗口=xxx、tabname=xxx,该新页面的元素则定义在该page下;
keyword | page | element | data |
右击 | 列表页 | 操作列 |
注意:
- context_click / 右击 关键字,通常用于操作按钮或链接文字;
keyword | page | element | data |
双击 | 列表页 | 图片 |
注意:
- double_click / 双击 关键字,通常用于操作按钮或链接文字;
- 双击操作若会打开新页面/新窗口,可以设置新页面/新窗口的名称:标签页名=xxx、新窗口=xxx、tabname=xxx,该新页面的元素则定义在该page下;
keyword | page | element | data |
移动到 | 列表页 | 名称 |
注意:
- move / 移动到 关键字,通常用于鼠标悬浮于某元素上、显示更多内容的场景;
keyword | page | element | data |
拖拽 | 列表页 | 记录1|记录2 |
注意:
- drag and drop / 拖拽 关键字,常用于将元素移动到另一元素处;
- 测试用例中element列使用|号分隔开多个元素;
keyword | page | element | data |
滑动 | 解锁页 | 滑动条 | x=500,,y=100 |
注意:
- swipe / 滑动 关键字,常用于拖动元素移动一段距离;
keyword | page | element | data |
刷新 | 列表页 |
注意:
- refresh / 刷新 关键字,常用于刷新当前页面;
keyword | page | element | data | output |
获取 | 列表页 | 名称 | name=text |
注意:
- obtain / 获取 关键字,常用于获取元素某些属性值,以便进行结果检查;
- 获取的值保存在用例output列的变量中;
- 应用场景:新建用户后,获取列表中该新记录的“名称”字段值,与新建时的输入作对比检查;
keyword | page | element | data |
判断 | 首页 | 欢迎文案 |
注意:
- judge / 判断 关键字,常用于检查元素是否存在/不存在;
- 用例中data列为空,则默认判断元素是否存在;
- 可设置判断元素是否不存在:存在=否、exist=no
keyword | page | element | data |
脚本 | 通用 | window.alert('这是一个测试Alert弹窗'); |
注意:
- script / 脚本 关键字,常用于执行Js脚本;
- js脚本写在用例element列;
keyword | page | element | data | output |
调用 | 通用 | mytime=time.getTime() | ||
调用 | balance=thousands.thousands(<balance>+1000) | |||
调用 | result=thousands.thousands(a1,a2) |
注意:
- call / 调用 关键字,常用于调用自定义方法;
- 具体调用的方法,写在用例中output列,以 模块.方法(参数1,参数2) 的格式进行调用,模块必须位于sweetest.lib目录下;
- 调用自定义方法时,参数可以带变量,以<>括起来的为变量,也可以带计算表达式;
- 应用场景:在测试用例中获取当前时间作为测试数据;
keyword | page | element | data |
执行 | 用例片段 | SNIPPET_001 | name=tester,,pwd=123456 |
注意:
- 测试用例中可以把公共部分单独抽离出来定义为用例片段(ID以"SNIPPET"开头),后续可使用execute / 执行 关键字调用用例片段;
- 用例片段中的测试数据可以先用变量赋值,外层用例执行用例片段时再把具体的变量值放在用例data列传进去;
- 可设置用例片段循环执行次数:
- 用 SNIPPET_ID 表示运行1次用例片段;
- 用 SNIPPET_ID*N 表示循环运行N次用例片段;
- 可设置用例片段循环结束条件:循环结束条件=成功、condition=pass、循环结束条件=失败、condition=fail;
- 循环结束条件 优先级高于 循环执行次数:
- 只要满足循环结束条件,即使未到达循环执行次数,也结束循环;
- 若到达循环执行次数,但没有满足循环结束条件,则结束循环;
- 可设置中断测试策略:
- 用 SNIPPET_IDN 表示某次循环失败后继续运行;
- 用 SNIPPET_ID*N 表示某次循环失败后直接退出;
- 允许嵌套调用用例片段;
- execute / 执行 关键字还可以用于变量赋值,变量可用于后续的测试数据中;
keyword | page | element | data |
执行 | 通用 | 变量赋值 | name=tester,,pwd=123456 |
输入 | 登录页 | 用户名 | <name> |
keyword | page | element | data |
对话框 | 通用 | 确认 |
keyword | page | element | data |
对话框 | 通用 | 输入 | 审核通过 |
注意:
- web测试过程中经常会遇到弹窗,包括alert、confirm、prompt弹窗;
- message / 对话框 关键字,对应的page=通用,element取值:确认、取消、关闭、输入;
- prompt弹窗的输入操作,会自动点击确认按钮,故无需再写确认步骤;
keyword | page | element | data |
关闭 | 标签页 | ||
关闭 | 浏览器 |
注意:
- close /关闭 关键字对应的元素可以为 浏览器 或 browser,表示关闭浏览器;
- close /关闭 关键字对应的元素可以为 标签页 或 tab,表示关闭浏览器;
- csv格式文件;
- 可存放全局变量;
- 最后字段!=flag:变量值全部读取使用;
- 最后字段=flag:本次测试读取flag!=Y的变量值使用,读取值后置flag=Y,下次自动化测试时不再使用该值,适用于数据需唯一的场景;需及时维护添加数据;
- 若最后字段=flag且不存在flay!=Y的记录,则本次测试没有数据可以使用,用例执行会失败;
- 打开(open) / 点击(click) / 双击(double_click) 操作若有新开窗口,将注册新窗口(将页面名字与窗口句柄绑定);
- 如果有提供新页面名字,则使用该名字,否则使用默认名字:HOME;
- 若存在同名窗口(页面名字相同,但窗口句柄不同),则关闭旧窗口,将页面名字与当前窗口绑定;
- 测试步骤中的非 对话框(message) 操作,会判断是否需要窗口切换;
- 测试步骤中的 页面(page) 未绑定任何窗口,则绑定到当前窗口,不进行窗口切换;
- 测试步骤中的 页面(page) 已绑定当前窗口,则不进行窗口切换;
- 测试步骤中的 页面(page) 已绑定已有窗口且非当前窗口,则进行窗口切换;
- 页面“通用”是不绑定到任何窗口的,相关的测试步骤是直接在当前窗口操作的;
result = AutoTest.get_result()
print(result)
Output:
{
"testAll": 6, #测试用例总数
"testPass": 3, #测试通过的用例数
"testFail": 0, #测试失败的用例数
"testSkip": 3, #测试跳过的用例数
"beginTime": "2019-06-12 16:43:31", #测试开始时间
"totalTime": "80.51s", #测试时长
"testResult": [ #测试结果
{
"testsuite": "baidu", #测试用例集
"testcases": [ #测试用例
{
"id": "BAIDU_001", #用例ID
"title": "搜索:自动化测试", #用例标题
"result": "成功", #用例结果
"setup": { #用例准备工作(即setup用例内容)
"id": "HOME_001",
"title": "打开百度",
"result": "Pass",
"steps": [
{
"control": "",
"no": "1",
"keyword": "EXECUTE",
"page": "用例片段",
"element": "SNIPPET_003",
"data": {},
"expected": {},
"output": {},
"score": "OK",
"remark": "",
"custom": "",
"snippets": [
]
}
]
},
"steps": [
{
"control": "", #测试步骤逻辑控制符(if、else、then)
"no": "1", #测试步骤
"keyword": "INPUT", #关键字
"page": "百度搜索页面", #测试页面
"element": "百度搜索页面^搜索框", #测试元素
"data": { #测试数据
"text": "自动化测试",
"text1": ""
},
"expected": {}, #预期结果
"output": {}, #输出数据
"score": "OK", #执行结果
"remark": "", #备注说明
"custom": "" #frame信息
},
{...},
...
],
"teardown": { #用例清理工作(即teardown用例内容)
"id": "HOME_002",
"title": "打开百度",
"result": "Pass",
"steps": [
{
"control": "",
"no": "1",
"keyword": "EXECUTE",
"page": "用例片段",
"element": "SNIPPET_003",
"data": {},
"expected": {},
"output": {},
"score": "OK",
"remark": "",
"custom": "",
"snippets": [
]
}
]
},
},
{...},
...
}
{ "testsuite": "baidu", #测试用例集
"testcases": [ ] #测试用例
}]
}
sweetest重构工具的产生,是在开源项目 喜文测试-sweetest 的基础上扩展、优化(功能上只有少数变化,更多的是代码细节的修改、源码中有注释),受益匪浅,非常感谢!
- 喜文测试-sweetest: https://github.com/tonglei100/sweetest/