pytest学习
pytest
参考
https://blog.csdn.net/stetstet/article/details/119221464
https://blog.csdn.net/lovedingd/article/details/98952868
示例
运行方式
main方式运行
import pytest
if __name__ == "__main__":
## pytest.main(['-s','test_example01.py'])
pytest.main(['-s','test_example01.py'])
文件右键运行
命令行运行
命令 | 说明 | tag |
---|---|---|
pytest test_se.py -s | 显示print内容 | ==辅助== |
pytest test_se.py --reruns NUM |
重试运行cases。NUM=重试的次数 | ==执行== |
pytest -x | 第x次失败,就停止测试 | ==执行== ==失败== |
pytest --maxfail=n | 出现n个失败就终止测试 | ==执行== ==失败== |
pytest testing/ | 指定测试目录 | ==执行== |
pytest -k "MyClass” | 通过关键字表达式过滤执行 | ==执行== ==失败== |
pytest test_mod.py::test_func | 通过 node id 指定测试用例(nodeid由模块文件名、分隔符、类名、方法名、参数构成) | ==执行== ==失败== |
常用命令参数
- 执行当前文件夹下面的所有文件
pytest 文件名/
- 执行具体的某一测试文件
pytest 脚本名称.py
- -k 匹配用例名称
执行测试用例名称包含qq的所有用例
pytest -k qq test_1.py
- -k 根据用例名称排除某些用例
pytest -s -k "not qq" test_1.py
- -k 同时匹配不同的用例名称
pytest -s -k "wechat or webo" test_1.py
- 运行.py模块里面的某个函数
pytest test_mod.py::test_func
- 运行.py模块里面,测试类里面的某个方法
pytest test_mod.py::TestClass::test_method
- -x 遇到错误时停止测试
pytest -x test_class.py
- 当用例错误个数达到指定数量时,停止测试
pytest --maxfail=1
- -q 简单打印,只打印测试用例的执行结果
pytest -q test_1.py
- -s 详细打印,-v 更加详细的打印,通过的.用pass表示
pytest -s test_1.py
pytest -v test_1.py
使用要求
钩子函数(前置、后置)
不同的作用范围
- 模块级别
在当前模块下所有用例执行前去调用登录接口
def setup_module():
buyer_login()
print('在当前模块下所有用例执行前去调用登录接口')
def teardown_module():
print('在当前模块下所有用例执行后要做的后置动作')
- 函数级别
每一条测试用例执行前后都要去执行的动作
def setup_function():
print('每条测试用例执行前都会执行')
def teardown_function():
print('每条测试用例执行后都会执行')
- 类级别
默认的编写规则是类以Test开头
class TestCart:
def setup_class(self):
print('当前类下所有的测试用例执行前调用登录接口')
def teardown_class(self):
print('当前类下所有的测试用例执行后执行的动作')
参数化(数据驱动)
普通方式
import pytest test_data = [ [1, 4, 5], [2, 4, 6], [1, -1, 0], [1, 4, 9] ]
def add(a, b):
return a + b
@pytest.mark.parametrize('val_a,val_b,exp_result', test_data)
def test_add(val_a, val_b, exp_result):
assert add(val_a, val_b) == exp_result
笛卡尔积
import pytest
test_data1 = ['aaa', 'cc']
test_data2 = ['zzzzzzz', 'xxxx', 'd']
@pytest.mark.parametrize('val_a', test_data1)
@pytest.mark.parametrize('val_b', test_data2)
def test_add(val_a, val_b):
print (val_a,val_b)
conftest.py
名称固定。pytest运行时自动扫描该文件,获取配置的钩子函数
使用场景
- 如果某个前置和后置在脚本里大量使用,则可以在conftest.py中统一定义一个fixture,来进行使用
- 但是如果某个前置或者后置,仅仅只在当前测试用例使用,那么就可以使用之前的setup_xxx和teardown_xxx
解决入参存在中文,导致的运行标题乱码
创建文件 conftest.py
,复制以下代码,重新运行测试用例
# 解决中文乱码 from typing import List
def pytest_collection_modifyitems(
session: "Session", config: "Config", items: List["Item"]
) -> None:
# item表示每个测试用例,解决用例名称中文显示问题
for item in items:
item.name = item.name.encode("utf-8").decode("unicode-escape")
item._nodeid = item._nodeid.encode("utf-8").decode("unicode-escape")
管理fixture
- scope表示该fixture的作用域
session表示在本次pytest执行时,只会被执行一次 module表示在每个模块执行时,只会被执行一次 class表示在每个测试类执行时,只会被执行一次 function表示在每个测试用例执行时,都会被执行 package和session基本上差不多 - autouse表示该fixture函数是否被自动调用,默认是False
设置为True那么会根据作用域自动完成调用 设置为False则需要手动完成调用 手动调用分为两种: 1.@pytest.mark.usefixtures('get_buyer_token') # 括号里就是fixture函数的名称 2.在测试用例函数参数中直接写上fixture函数的名称
自动调用
conftest.py中加入
@pytest.fixture(scope='session', autouse=True) def session_start(): print("session级别的:开始测试啦")
@pytest.fixture(scope='function', autouse=True)
def func_start():
print("function级别的:准备执行测试")
手动调用
- 方法一:使用装饰器,可加载测试类上,也可加载测试方法上
conftest.py中加入
@pytest.fixture(scope='function', autouse=False)
def func_start():
print("function级别的:准备执行测试")
测试文件中加入
import pytest
def test_1():
print('test1')@pytest.mark.usefixtures('func_start')
def test_2():
print('test2')
def test_3():
print('test3')
- 方法二:直接写入方法入参中
conftest.py中加入
@pytest.fixture(scope='function', autouse=False)
def func_start():
print("function级别的:准备执行测试")
测试文件中加入
def test_3(func_start):
print('test3')
存放数据
conftest.py中加入
@pytest.fixture(scope='function', autouse=False)
def func_start():
return '测试数据'
测试文件中加入
def test_3(func_start):
print('str: '+func_start)
yield 后置处理
conftest.py中加入
@pytest.fixture(scope='function')
def some_data():
# 前置动作
print('get data')
# 返回数据
yield 'data_id'
# 后置动作
print('clear data')
测试文件中加入
def test_4(some_data):
print(f'the id of the good is : {some_data}')
pytest配置文件
固定名称pytest.ini,便于指定范围的用例文件执行
[pytest]
addopts = -sv
testpaths = ./
python_files = test_*.py
python_classes = Test*
python_function = test_*
创建一个py文件,加入以下代码,自动扫描路径下pytest.ini,根据pytest.ini的配置执行测试
if __name__ == '__main__':
pytest.main()
多断言pytest-assume
即使第一条断言失败,也会判断之后的断言
需装插件pytest-assume
def test_1():
string1 ='iaosdf'
pytest.assume(len(string1)>8,f'实际是{False},预期是{False}' )
string1 = 'iis'
pytest.assume(len(string1)>8,f'第二次:实际是{False},预期是{False}' )
string1 = '333333ffdfdfddfd'
pytest.assume(len(string1)>8,f'第二次:实际是{True},预期是{True}' )
Allure测试报告
需装插件allure-pytest
使用
- 在pytest.ini文件中追加参数
[pytest]
addopts = -sv --alluredir ./report/data --clean-alluredir
testpaths = ./
python_files = test_*.py
python_classes = Test*
python_function = test_*
- 使用命令行
pytest -sv --alluredir ./report/data --clean-alluredir [pytest测试文件名.py]
会生成./report/date目录,有一堆json文件
- allure命令行工具配置
pycharm中terminal进入到report同级目录执行命令
allure generate ./report/data -o ./report/html --clean
也可以集成至run.py(文件名非固定)文件中
import os
import pytest
if __name__ == '__main__':
pytest.main()
os.system('allure generate ./report/data -o ./report/html --clean')
需要在pycharm中打开index.html选择浏览器打开
错误信息
pytest的退出码有六种:
0:所有的测试case都运行成功
1:部分(或者全部)测试case运行结果失败
2:测试执行过程被用户中断(即跑测试case过程中,用户进行Ctrl + C命令)
3:发生了内部错误(可能是python脚本某些运行时错误)
4:pytest命令行使用错误(可能没有传递正确的命令行参数)
5:实际没有执行case(可能是执行了一个空的测试类)
遇到的问题
- 运行找不到测试方法