一、初级阶段
- 先不写了
二、中级阶段
pytest参数化
- 什么是参数化?
- 通过参数的方式传递数据,从而实现数据和脚本分离。
- 并且可以实现用例的重复生成与执行。
- 使用场景?
- 一般是一个完整的输入测试场景,通过用不同的输入数据,产生不同的情况
- 如:登录场景的正向、逆向case等
- 实现方式
- 装饰器:@pytest.mark.parametrize
@pytest.mark.parametrize("username,password",[["right","right"], ["wrong","wrong"]]) def test_param(username,password): login(username,password)
- 装饰器:@pytest.mark.parametrize
- 参数化支持的几种使用情况
-
单参数
- 单个参数使用时,放在列表中
search_list = ['appium','selenium','pytest'] @pytest.mark.parametrize('name',search_list) def test_search(name): assert name in search_list
- 单个参数使用时,放在列表中
-
多参数
- 将数据放在列表嵌套元组中
- 将数据放在列表嵌套列表中
# 数据放在元组中 @pytest.mark.parametrize("test_input,expected",[ ("3+5",8),("2+5",7),("7+5",12) ]) def test_mark_more(test_input,expected): assert eval(test_input) == expected # 数据放在列表中 @pytest.mark.parametrize("test_input,expected",[ ["3+5",8],["2+5",7],["7+5",12] ]) def test_mark_more(test_input,expected): assert eval(test_input) == expected
-
用例重命名
- 通过ids参数,将别名放在列表中
@pytest.mark.parametrize("test_input,expected",[ ("3+5",8),("2+5",7),("7+5",12) ],ids=['add_3+5=8','add_2+5=7','add_3+5=12']) def test_mark_more(test_input,expected): assert eval(test_input) == expected
- 用例名输出控制台
# 创建conftest.py 文件 ,将下面内容添加进去,运行脚本 def pytest_collection_modifyitems(items): """ 测试用例收集完成时,将收集到的用例名name和用例标识nodeid的中文信息显示在控制台上 """ for i in items: i.name=i.name.encode("utf-8").decode("unicode_escape") i._nodeid=i.nodeid.encode("utf-8").decode("unicode_escape")
- 通过ids参数,将别名放在列表中
-
笛卡尔积
- 如:两组数据
a=[1,2,3]
b=[a,b,c]
对应有几种组合形势 ?
(1,a),(1,b),(1,c)
(2,a),(2,b),(2,c)
(3,a),(3,b),(3,c) - 那在这其实就可以用笛卡尔积的方式参数化,例如下边:
@pytest.mark.parametrize("b",["a","b","c"]) @pytest.mark.parametrize("a",[1,2,3]) def test_param1(a,b): print(f"笛卡积形式的参数化中 a={a} , b={b}")
- 如:两组数据
-
Mark 标记测试用例
- 什么是Mark标记?
- 为用例打标记,比如在执行冒烟用例时,我就可以单独执行标记的case。
- 也可以把一个web项目划分多个模块,然后指定模块名称执行
- 使用场景
- 执行冒烟用例回归
- 指定模块回归
- 实现方式
- 在测试用例方法上加 @pytest.mark.标签名
- 指令
- -m 执行自定义标记的相关用例
pytest -s test_mark_zi_09.py -m=webtest pytest -s test_mark_zi_09.py -m apptest pytest -s test_mark_zi_09.py -m "not ios"
- -m 执行自定义标记的相关用例
设置用例失败自动跳过
-
为什么使用跳过?
- skip - 始终跳过该测试用例
- skipif - 遇到特定情况跳过该测试用例
- xfail - 遇到特定情况,产生一个“期望失败”输出
- 这是 pytest 的内置标签,可以处理一些特殊的测试用例,不能成功的测试用例
-
skip使用场景
- 调试时不想运行这个用例
- 标记无法在某些平台上运行的测试功能
- 在某些版本中执行,其他版本中跳过
-
例子:当前的外部资源不可用时跳过,如果测试数据是从数据库中取到的, 连接数据库的功能如果返回结果未成功就跳过,因为执行也都报错
- 解决 1:添加装饰器
@pytest.mark.skip @pytest.mark.skipif
- 解决 2:代码中添加跳过代码
pytest.skip(reason)
- 解决 1:添加装饰器
-
xfail使用场景
-
当预期结果为失败(fail)时,跳过
-
使用方式
添加装饰器@pytest.mark.xfail
运行用例
- 场景
- 运行 某个/多个 用例包
执行包下所有的用例:pytest/py.test [包名]
- 运行 某个/多个 用例模块
执行单独一个 pytest 模块:pytest 文件名.py
- 运行 某个/多个 用例类
运行某个模块里面某个类:pytest 文件名.py::类名
- 运行 某个/多个 用例方法
运行某个模块里面某个类里面的方法:pytest 文件名.py::类名::方法名
- 运行 某个/多个 用例包
- 运行结果分析
- 常用的:fail/error/pass
- 特殊的结果:warning/deselect
用例调度与运行
- 命令行参数
- 使用缓存状态
- –lf(–last-failed)` 只重新运行故障。
- –ff(–failed-first)` 先运行故障然后再运行其余的测试
- 使用缓存状态
pytest命令行常用参数
```
—help
-x 用例一旦失败(fail/error),就立刻停止执行
--maxfail=num 用例达到
-m 标记用例
-k 执行包含某个关键字的测试用例
-v 打印详细日志
-s 打印输出日志(一般-vs一块儿使用)
—collect-only(测试平台,pytest 自动导入功能 )
```
代码执行pytest
- 使用 main 函数
if __name__ == '__main__': # 1、运行当前目录下所有符合规则的用例,包括子目录(test_*.py 和 *_test.py) pytest.main() # 2、运行test_mark1.py::test_dkej模块中的某一条用例 pytest.main(['test_mark1.py::test_dkej','-vs']) # 3、运行某个 标签 pytest.main(['test_mark1.py','-vs','-m','dkej']) 运行方式 `python test_*.py `
- 使用 python -m pytest 调用 pytest(jenkins 持续集成用到)
pytest异常处理
- 常用的异常处理
- try…except
try: 可能产生异常的代码块 except [ (Error1, Error2, ... ) [as e] ]: 处理异常的代码块1 except [ (Error3, Error4, ... ) [as e] ]: 处理异常的代码块2 except [Exception]: 处理其它异常
- pytest.raises()
- 为什么使用它?
- 可以捕获特定的异常
- 获取捕获的异常的细节(异常类型,异常信息)
- 发生异常,后面的代码将不会被执行
def test_raise(): with pytest.raises(ValueError, match='must be 0 or None'): raise ValueError("value must be 0 or None") def test_raise1(): with pytest.raises(ValueError) as exc_info: raise ValueError("value must be 42") assert exc_info.type is ValueError assert exc_info.value.args[0] == "value must be 42"
- 为什么使用它?
- try…except