一、pytest 介绍、运行、参数化和数据驱动、Fixture
pytest安装与介绍
- 官网 : pip install -U pytest
- 查看版本号:pytest –version
为何选择pytest
- 兼容unittest
- 定制化插件开发
pycharm 配置github
- VSC–Git–Remotes…
pycharm pytest 配置
- settings–搜索pytest–Python integrated Tools–testing–选择pytest:根据黄色叹号fix安装pytest安装到环境
pytest命名规则
- python风格规范:
https://zh-google-styleguide.readthedocs.io/en/latest/google-python-styleguide/python_language_rules/ - pytest:文件名: test_开头 或者 _test 结尾
- 类名:Test 开头
- 方法(类内)/函数名(类外):test_开头
- 注意:测试类中不可以添加__init__构造函数
pytest运行测试用例
1 – pycharm
2 – 命令行运行方式 – 推荐(以便后续持续集成)
可能会遇到的找不到模块的路径问题,需要在导包之前引入路径 import sys sys.path.append(\’路径\’)
- 运行包: pytest (pytest会自动检索当前目录下所有符合规则的测试用例)
- 运行一个模块:pytest -vs test.py
- 只执行某一条测试用例:pytest 目录/模块.py::类名::用例名
常用命令行参数
- pytest –help: 获取所有参数列表
- -x: 用例一旦失败(fail/error),就立刻停止使用场景:一般用于冒烟测试冒烟测试最早由google提出的一个概念,一般针对每日构建的版本,对系统的基本功能进行简单测试的测试类型,主要强调程序主体的功能
标记测试用例:mark
- 场景:只执行符合要求的某一部分用例,把项目分为多个模块,然后指定模块名称执行
- 解决:在测试用例的上方加上pytest装饰器:@pytest.mark.标签名
- 执行 -m 标记自定义的相关用例pytest -s test_mark.py -m=标签名
- pytest -s test_mark.py -m 标签名
- pytest -s test_mark.py -m \”not 标签名\” (not:逻辑运算,表示不是标签名的都去执行)
跳过(skip)及预期失败(xfail)
pytest的内置标签,可以处理一些特殊的、及不能成功的测试用例
-
ship: 始终跳过该用例
方式1: @pytest.mark.skp(reason=\”代码没有实现\”)
- 方式2:在测试用代码中添加判断的语句
def test_demo():if not login():pytest.skip(\"未登录无法运行该用例\")
skipif: 遇到特定情况跳过该测试用例,需要给定条件
@pytest.mark.skipif(条件=\"\", reason=\"打印提示信息内容\")
xfail: 遇到特殊情况,产生一个“期望失败”输出
运行结果分析
- 常用:fail error pass
- 特殊结果:warning
- deselect
pytest常用执行参数
———-
更多用法使用 pytest –help 查看帮助文档
用例匹配:
- pytest -k \”add\” 匹配所有用例名称中包含add的用例
- pytest -m mark 标签名、标记:在测试用例上添加装饰器 @pytest.mark.login
pytest有很多自带的标签,我们自己定的标签,如login 他会不识别,所以在运行后会有 deselected、warnings
警告信息,需要创建pytest配置文件:pytest.inipytest.ini[pytest]# markers 帮助文档解释:为你的测试用例添加标签markers = loginsearch\'个人中心\'
pytest.ini:
- pytest.ini是pytest的配置文件文件内容要以 [pytest] 开头
- 修改pytest模块名、类名、用例名称规则(以什么开头:如pytest文件名和用例名默认以test_* 开头或结尾)
python_files (args):用于Python测试模块发现的全局样式文件模式(文件名)
-
python_classes (args):前缀或glob名称,用于发现Python测试类(类名)
-
python_functions (args): Python测试函数和方法发现的前缀或glob名称(方法名)
-
addopts:添加一个或多个命令行参数,如,添加 -vs 命令参数后:运行pytest test_case.py 等同于 pytest -vs test_case.py
pytest.ini
[pytest]# 为测试用例添加标签markers = loginsearch# 修改pytest模块名、类名、用例名称规则(以什么开头:如pytest文件名和用例名默认以test_* 开头或结尾)pytest_files = test_* *_test check_*pytest_functions = test_* *_test check_*# 添加一个或多个命令行参数,如,添加 -vs 命令参数后:运行pytest test_case.py 等同于 pytest -vs test_case.pyaddopts =ad8-vs --alluredir=./result
pytest框架结构
- setup() teardown() 类方法运行前后被调用模块级别:setup_module/teardown_module 全局的、优先级最高
- 函数级别:setup_function/teardown_function 只在函数用例生效(与方法的级的区别:不在类中)
- 类级别:setup_class/teardown_class 只在类中前后运行一次(在类中)
- 方法级别:setup_method/teardown_method 开始与方法的始末、每个方法运行强后都运行一遍(与函数级别的区别:方法在类中,函数不在类中)
pytest参数化与数据驱动
- 参数化一般与数据驱动一起使用,参数化:将变化的测试数据以参数的形式传入到测试方法,待测试的输入和输出是一组数据,可以把测试数据组织起来,用不同的测试数据调用相同的测试方法
- 数据驱动:数据的改变从而驱动自动化的执行,最终引起测试结果的改变,测试数据的数据驱动、测试步骤的数据驱动、配置的数据驱动、po的数据驱动
pytest参数化
- pytest自带参数化概念加入参数化装饰器:@pytest.mark.parametrize(\”a, b, result\”, [1, 2, 3], [2, 3, 5], [0.3, 1, 1.3])ids参数:定义每一组测试数据生成的测试用例的别名,多少组数据就对应有多少个ids
import pytestimport yamldef add(a, b):return a + bclass TestDate:@pytest.mark.parametrize(\"a, b\", [(10, 20), (10, 30), (2, 6)])def test_param(self, a, b):print(a + b)@pytest.mark.parametrize([\"x\", \"y\"], [(10, 20), (10, 30)])def test_param2(self, x, y):# print(x + y)print(add1(x, y))@pytest.mark.parametrize(\"a\", \"b\", yaml.safe_load(open(r\'E:\\data.yaml\')))def test_param5(self, a, b):print(f\'相加等于{add(a, b)}\')
- 运行用例test_parm会得出三条测试用例数据:
pytest异常捕获
设计测试用例的时候要尽可能的区分类型,比如除数为0时,预期结果是抛出异常,则此用例pass,所以使用pytest捕获ZeroDivisionError异常,如果捕获到,则不报出异常 则此测试用例通过
with pytest.raises(ZeroDivisionError, TypeError):
fiad0xture高级用法
fixture 介绍:
- 官网 Features–Modular有详细介绍
- fixture又名测试桩子,类似setup teardown
- fixture 能做到setup teardown 做不到的事情
比如:一个模块里又很多用例,这些方法有的需要的前置条件不一样,如有的需要登录,有的不需要登录
def login():print(\'登录\')def test_search():print(\'搜索\')def test_search():print(\'购物\')def test_search():print(\'下单\')
-
如,如果使用setup teardown 只能是用例前后被调用,但是去搜索,首先不需要登录,所以就可以把login定义为fixture函数,然后传入到测试方法里
使用方法:在被重复调用的方法上加上@pytest.fixture()装饰器,如果某个方法需要登录,就传入login@pytest.fixture()def login():print(\'登录\')def test_search():print(\'搜索\')def test_search(login):print(\'购物\')def test_search(login):print(\'下单\')
- 在命令行中使用执行回溯,可以看到fixture执行的详细过程:pytest test_case.py –setup-show
fixture 作用
- Fixture是在测试函数运行前后,由pytest执行的外壳函数,代码可以定制,满足多变的测试需求,功能包括:定于传入测试中的数据集
- 配置测试前系统的初始状态
- 为批量测试提供数据源等
fixture用法
- 1- 类似setup teardown功能,但比setup teardown更灵活
- 2- 直接通过函数名调用或者装饰去@pytest.mark.usefixtures(\’test1\’)
- 3- 允许使用多个Fixture
- 4- 使用autouse自动应用,如果由返回值,需要穿fixture函数名
- 5- 作用域 session>module>class>function
使用– setup-show 回溯fixture的执行过程
- pytest test_case.py –setup-show
pytest yield
-
以上fixture记录的仅仅是setup,即测试用例运行前的操作,如果需要加上teardown的操作,需要在fixture函数中加入 yield
@pytest.fixture()def login():print(\'登录\') # 用例运行前登录yield # 相当与 return 可以返回一些数据 如 yield tokenprint(\'退出\') # 用例2b3c结束后退出
conftest + fixture
-
conftest.py文件,它主要是实现数据(fixture)共享的文件,名字是固定的。
1- 第一,conftest.py文件当中,它储存的都是fixture,就是给用例提供做前置准备工作和后置清理工作的一个东西;
- 2- 第二,conftest.py文件可以将它的fixtures共享到它自己目录下的所有用例,用例当中如果使用fixture的话,是不需用导入conftest.py这个文件的,会直接自动去查找;
- 3- 第三,conftest.py它是属于层级共享的,也就是说,一个自动化项目当中,可以在不同的包下面去创建conftest.py这个文件。
运行原则:就近原则,如果当前包下没有conftest.py就会往上一层找
fixture 参数化
二、常用插件、数据驱动、编写pytest插件、Allure
pytest常用插件
pip install pytest-ordering:控制用例的执行顺序
- 建议在编写测试用例的时候,能不让用例顺序执行就不顺序执行,这样可能会更好的暴露出问题
- 在测试方法上加上装饰器:@pytest.mark.run(order=2)
pip install pytest-dependency:控制用例的依赖关系
ps:设计测试用例的时候进可能不要让用例有顺序,不要让测试用例有依赖关系,如果无法做到,可以临时的用插件解决
-
官网 : https://pytest-dependency.readthedocs.io/en/latest/usage.html#basic-usage
-
设置一个用例和一个用例的依赖关系:一旦用例A失败,那么用例B也不会执行。比如:用例A为添加购物车,用例B为去结算,那么如果用例A没有登录就会执行失败,则用例B也就会执行失败
pytest-dependency 要做的就是,让他们之间存在一种依赖关系:一旦用例A失败,那么用例B也不会执行 SKIPimport pytest# 官方demo:@pytest.mark.dependency() # test_a测试用例设置了dependency依赖关系@pytest.mark.xfail(reason=\"deliberate fail\")def test_a():assert False@pytest.mark.dependency() # test_b测试用例设置了dependency依赖关系def test_b():pass@pytest.mark.dependency(depends=[\"test_a\"]) # test_c 依赖于测试用例 test_a。 因为test_a断言失败,所以test_c会跳过def test_c():pass@pytest.mark.dependency(depends=[\"test_a\"]) # test_d 依赖于测试用 test_bdef test_d():pass@pytest.mark.dependency(depends=[\"test_b\", \"test_c\"]) test_e依赖与测试用例 test_b 和 test_c 因为test_b通过了,但test_c跳过,所以test_e也会跳过def test_e():pass
pip install pytest-xdist:分布式并发执行测试用例
- 机制:哪个cpu先执行完一个任务,就下发另一个任务pytest -n 3 :3=当前系统的cpu个数,如果不知系统有多少个cpu或者不知道可以承载多少个进程,使用 pytest -n aotu 自动分发空闲的进程
pip install pytest-rerunfailures:失败重跑
- pytest –reruns 5 : 如果用例失败,就会重新跑5次
- pytest –reruns 5 –reruns-delay 2:用例失败后等待2秒后再执行,或者再用例方法上加上装饰器@pytest.mark.flaky(5, reruns-delay=2)
pip install pytest-assume:多重校验
- 通常一个测试方法中会写多个断言,如果第一条断言过不去,下面的就不执行了,如果我们想报错也往下执行,使用pytest提供的断言方法方法pytest.assume(1==4) 相当于 assert 1==4 # 此方法应用场景较少
pip install pytest-random-order:用例随机执行
- pytest –random-order-seed= 设置一个种子,使用比较少
pip install pytest-html:测试报告
- 后续使用allure,不做介绍
三、pytest高级用法 hook函数
pytest插件加载方式
-
内置plugin
从代码内部的 _pytest 目录加载External Libraries – site-package – _pytest – hookspec.pyhookspec.py 有很多方法 即我们所说的hook方法,我们可以改写hook方法来满足我们一些需求
外部插件(第三方插件)
- 通过setuptools entry points 机制发现的第三方插件(https://docs.pytest.org/en/latest/plugins.html)
conftest.py 存放的本地插件(重点):
- 自动模块发现机制
pytest –trace-config 查看当前pytest中所有的plugin(带有hook方法的文件)
Allure 生成测试报告
另起...