Pytest框架
介绍
1.简介
pytest是纯python编写的自动化测试框架,可以支持python语法编写测试用例,是一个非常成熟的全功能的Python测试框架。
主要有以下几个特点:
简单灵活,容易上手;
支持参数化;
能够支持简单的单元测试和复杂的功能测试,还可以结合selenium、requests做自动化测试;
pytest具有很多第三方插件,并且可以自定义扩展。
安装:pip install pytest
2.第一个pytest用例
def test01():
assert 1 == 1
3.pytest命名规范
测试模块:以 test 开头命名,如:test_login.py,或以 _test 结尾;
测试类:必须以Test开头命名,且测试类中不能有 init 方法;
测试方法/测试函数:必须以test开头。
4.常用参数
可以通过pytest -h来查看所有可用参数。pytest的参数有很多,下面是归纳一些常用的参数:
无参数:读取路径下符合条件的所有类、函数、方法全部执行;
-v:打印详细运行日志;
-s:输出调试信息,包括print打印的信息;命令行输入:pytest -s;
-x:运行用例失败立即停止运行
–maxfail
用例失败数达到某个设定的值停止运行
pytest --maxfail=[num]
-m 运行所有@pytest.mark.[标记名] 标记的用例
比如:用例标记 @pytest.mark.high
pytest -m=hign 或者 pytest -m hign。# 表示只执行有此标记hight的case。
pytest -m="hign or smoke" 或者 pytest -m "hign or smoke" # 表示两种标记都执行
–reruns=num:失败用例重跑num次。需要安装 pytest-rerunfailures 插件模块。
-n参数,启用多线程或分布式运行测试用例。需要安装pip install pytest-xdist 插件模块。
命令行输入:pytest -vs -n=2
-k: 指定运行某个或某些用例
pytest -k ‘类名’
pytest -k ‘方法名’
pytest -k ‘类名 and not 方法名’ # 运行类里所有方法,不包含某个方法
命令行输入:pytest -vs -k=01
python程序运行pytest:
caseNameString = " or ".join(caseNameList)
cmd = f"python -m pytest -k \"{caseNameString}\" --alluredir ./allure --clean-alluredir"
os.system(cmd)
-k的值支持中文的哟~
参数-n,启用多线程或分布式运行测试用例。需要安装pip install pytest-xdist 插件模块。
命令行输入:pytest -vs -n=2
5.实现数据驱动
Pytest 测试框架的数据驱动是由 pytest 自带的pytest.mark.parametrize()来实现的。
@pytest.mark.parametrize() 装饰器接收两个参数:
第一个参数以字符串的形式存在,它代表能被测试函数所能接受的参数,如果被测试函数有多个参数,则以逗号分隔;
第二个参数用于保存测试数据。如果只有一组数据,以列表的形式存在,如果有多组数据,以列表嵌套元组的形式存在(例如: [1,1]或者[(1,1), (2,2)])。
实例
class Test01:
@pytest.mark.parametrize('a,b,expect',[(1,1,1),(2,3,5)])
def test_001(self,a,b,expect):
print('测试a+b的结果')
assert a+b==expect
@pytest.mark.parametrize('c,d,expect', [(2, 4, 2), (9, 10, 1)])
def test_002(self,c,d,expect):
assert d-c==expect
if name == 'main':
pytest.main([file, '-k','test_001'])
6.pytest fixtures
6.1 fixture用途
fixture主要用来做初始化环境以及测试结束后的数据清除。
pytest fixture与setup,teardown功能一样,但比之更加灵活,完全可以代替setup,teardown。
6.2 fixture参数详解
@pytest.fixture(scope='function',params=None,autouse=False,ids=None,name=None)
yield
fixture装饰器,相当于setup,测试用例的前置
- scope: 有四个级别参数'function(默认)'、'class'、'module'、'session'。
- params:一个可选的参数列表,列表中每个数据都可以作为用例的输入。也就说有多少数据,就会形成多少用例。可以通过request.param来获取该次调用的参数。
- autouse:如果True,自动调用fixture功能。如果为False则需要调用fixture。
- ids:每个字符串id的列表,每个字符串对应于params这样他们就是测试ID的一部分。如果没有提供ID它们将从params自动生成。
- name:fixture的名称。这默认为装饰函数的名称。如果fixture在定义它的统一模块。
yield:这个关键字之后的代码相当于teardown,测试用例的后置。
6.3 fixture的作用范围
fixture里面有个scope参数可以控制fixture的作用范围:session>module>class>function
-function:每一个函数或方法都会调用
-class:每一个类调用一次,一个类中可以有多个方法
-module:每一个.py文件调用一次,该文件内又有多个function和class
-session:是多个文件调用一次,可以跨.py文件调用,每个.py文件就是module
6.4 调用fixture的三种方法
方式1:函数或类里直接传fixture的函数名称
@pytest.fixture()
def test_fixture(self):
print('\n用例开始执行fix1\n')
yield
print('\n用例执行结束fix1\n')
def test_a(self, test_fixture):
print('runing')
assert 1 == 1
方式2: 使用装饰器@pytest.mark.usefixtures()修饰需要运行的用例(可以叠加使用多个装饰器)
@pytest.fixture()
def test_fixture(self):
print('\n用例开始执行fix1\n')
yield
print('\n用例执行结束fix1\n')
@pytest.fixture()
def test_fixture2(self):
print('\n用例开始执行fix2\n')
yield
print('\n用例执行结束fix2\n')
@pytest.mark.usefixtures('test_fixture')
@pytest.mark.usefixtures('test_fixture2')
def test_b(self):
assert 1==1
执行结果
7.skip – 跳过测试
7.1 pytest.skip() 用于函数内,跳过测试用例
@pytest.mark.parametrize('a,b,expect',[(1,1,1),(2,3,5)])
def test_001(self,test1,a,b,expect):
pytest.skip('跳过此测试用例')
assert a+b==expect
用于函数外,跳过测试用例
@pytest.mark.skip(reason="功能未实现")
@pytest.mark.parametrize('a,b,expect',[(1,1,1),(2,3,5)])
def test_001(self,test1,a,b,expect):
assert a+b==expect
用在函数外,条件condition为True时,跳过用例
@pytest.mark.skipif(condition=True,reason="功能未实现")
@pytest.mark.parametrize('a,b,expect',[(1,1,1),(2,3,5)])
def test_001(self,test1,a,b,expect):
assert a+b==expect
8.rerunfailure–失败重跑,插件pytest-rerunfailures
安装
前提条件: pytest (>=5.3) 和python >=3.6
安装:pip install pytest-rerunfailures
查看安装版本:pip show pytest-rerunfailures
pytest-rerunfailures 使用
命令行参数: --reruns n(重新运行次数)–reruns-delay m(等待运行秒数)
使用装饰器: @pytest.mark.flaky(reruns=5, reruns_delay=2)
命令行实例
!/usr/bin/env python3
!coding:utf-8
import pytest
import random
def test_simple_assume():
每次case运行的值为1或者2,具有随机性
r = random.randint(1, 3)
assert r == 1
if name == 'main':
pytest.main(['Test_demo01.py', '-v', '--reruns=2', '--reruns-delay 2'])
配置执行次数越多(如--reruns=2),执行的成功率越高。
命令行参数:
--reruns n(重新运行次数),--reruns-delay m(等待运行秒数)
9.Mark装饰器之order执行顺序
需要先安装插件
cmd命令窗口:pip install pytest-ordering
在pycharm中File-->settings-->Project-->Python Interpreter-->点击+号-->搜索pytest-ordering安装。
查看安装版本:pip show pytest-ordering
使用方法:
控制用例执行顺序的方法;
在需要调整用例执行顺序的函数(或方法)前增加,如@pytest.mark.run(order=x),x表示数字;
执行顺序,由小到大、由正到负、未标记的在正数后、负数前执行,顺序为:1,2,3,无标记,-3,-2,-1;
实例
import pytest
class Test_Class3():
@pytest.mark.run(order=2)
def test_case1(self):
print("测试方法1")
@pytest.mark.run(order=1)
def test_case2(self):
print("测试方法2")
@pytest.mark.run(order=3)
def test_case3(self):
print("测试方法3")
if name == 'main':
pytest.main(['Test_demo02.py' '-s'])
10.setup、teardown
setup_class()和 teardown_class()函数
需要定义在测试类中,定义在类外不起作用。
setup_class()定义场景,比如:创建日志对象,创建数据库的连接,创建接口的请求对象等。
teardown_class()定义场景,比如:销毁日志对象,销毁数据库的连接,销毁接口的请求对象。
"""
函数需要定义在测试类中,定义在类外不起作用。
setup_method()和 teardown_method(),在每个测试方法之前/之后执行。定义场景,比如:打开浏览器/关闭浏览器。
setup_class()定义场景,比如:创建日志对象,创建数据库的连接,创建接口的请求对象等。
teardown_class()定义场景,比如:销毁日志对象,销毁数据库的连接,销毁接口的请求对象。
"""
import pytest
class Test_setUp_tearDown:
# 方法级,前置函数
def setup_method(self):
# print("setup_method(self):在每个测试方法之前执行")
print("在每个测试方法之前执行")
# 方法级,后置函数
def teardown_method(self):
# print("teardown_method(self):在每个测试方法之后执行\n")
print("在每个测试方法之后执行")
# 类级,前置函数
def setup_class(self):
# print("setup_class(self):每个测试类之前执行一次\n")
print("每个测试类之前执行一次")
# 类级,后置函数
def teardown_class(self):
# print("teardown_class(self):每个测试类之后执行一次")
print("每个测试类之后执行一次")
# 测试用例a
def test_a(self):
print("test_a方法")
assert True
# 测试用例b
def test_b(self):
print("test_b方法")
assert True
if name == 'main':
pytest.main()
pytest钩子函数
在pytest中,钩子函数是一种特殊的函数,用于在测试执行过程中的特定阶段插入自定义逻辑。pytest提供了许多内置的钩子函数,这些钩子函数允许您在测试的不同阶段进行自定义操作。以下是pytest中常用的钩子函数及其作用
1.作用在类以外的钩子函数
1 setup()/tear_down()
def setup(): print("这是一个setup")
def teardown(): print("这是一个teardown")
2 setup_module()/teardown_module()
setup_module():在测试模块开始之前运行,用于设置模块级别的资源或配置。可以在该钩子函数中执行一次性的模块设置。
teardown_module():在测试模块结束之后运行,用于清理模块级别的资源或配置。可以在该钩子函数中执行一次性的模块清理。
def setup_module(self):
print("这个是模块级别的setup_module")
def teardown_module(self):
print("这个是模块级别的teardown_module")
3 setup_function/teardown_function
每条用例执行前执行一次,不会作用于class中的test_case
setup_function():在每个测试函数开始之前运行,用于设置单个测试函数的资源或配置。可以在该钩子函数中执行每个测试函数的准备工作。
teardown_function():在每个测试函数结束之后运行,用于清理单个测试函数的资源或配置。可以在该钩子函数中执行每个测试函数的清理工作。
def setup_function()
print("setup_function模块中每条用例执行前执行一次!")
def teardown_function():
print("teardown_function 模块中每条用例执行后执行一次!")
2.作用在类中的钩子函数
setup_class():在每个测试类开始之前运行,用于设置单个测试类的资源或配置。可以在该钩子函数中执行每个测试类的准备工作。
teardown_class():在每个测试类结束之后运行,用于清理单个测试类的资源或配置。可以在该钩子函数中执行每个测试类的清理工作。
setup_method():在每个测试方法开始之前运行,用于设置单个测试方法的资源或配置。可以在该钩子函数中执行每个测试方法的准备工作。
teardown_method():在每个测试方法结束之后运行,用于清理单个测试方法的资源或配置。可以在该钩子函数中执行每个测试方法的清理工作。
3.pytest_runtest_makereport 钩子函数
pytest_runtest_makereport 是一个重要的钩子,它在测试运行期间被调用,用于生成测试报告。通过覆盖这个钩子,你可以自定义测试报告的行为,例如在测试失败时执行某些操作,或者收集额外的信息。
示例
import pytest
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
execute all other hooks to get the report object
outcome = yield
rep = outcome.get_result()
# we only look at actual failing test calls, not setup/teardown
if rep.when == "call" and rep.failed:
# 自定义逻辑:当测试失败时的操作
print(f"Test {item.name} failed")
# 你可以在这里添加更多的逻辑,例如记录日志、发送邮件等
在这个示例中,我们使用 @pytest.hookimpl 装饰器定义了一个 pytest_runtest_makereport 钩子函数。参数 tryfirst=True 表示这个钩子应该尽可能早地被调用,而 hookwrapper=True 表示这是一个钩子包装器,它会先执行其他注册的钩子函数,然后执行自己的逻辑。
钩子详解
item:当前正在运行的测试项。
call:当前测试阶段(setup、call 或 teardown)。
rep:测试报告对象,包含测试的结果信息。
示例中的逻辑
outcome = yield:这部分代码使用 yield 来执行其他注册的钩子函数,并获取它们的结果。这是 hookwrapper 的典型用法。
rep = outcome.get_result():获取测试报告对象。
条件判断:if rep.when == "call" and rep.failed: 检查当前阶段是否为测试执行阶段(而非 setup 或 teardown),并且测试是否失败。
自定义逻辑:在测试失败时执行自定义的逻辑,例如打印一条消息。
完整的插件示例
如果你需要将这个钩子放入一个插件中,可以创建一个 .py 文件(例如 my_plugin.py),并在其中定义这个钩子:
import pytest
class MyPlugin:
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(self, item, call):
execute all other hooks to get the report object
outcome = yield
rep = outcome.get_result()
# we only look at actual failing test calls, not setup/teardown
if rep.when == "call" and rep.failed:
# 自定义逻辑:当测试失败时的操作
print(f"Test {item.name} failed")
# 你可以在这里添加更多的逻辑,例如记录日志、发送邮件等
def pytest_configure(config):
config.pluginmanager.register(MyPlugin(), "my_plugin")
def pytest_unconfigure(config):
plugin = getattr(config, "my_plugin", None)
if plugin is not None:
config.pluginmanager.unregister(plugin)
在这个示例中,我们定义了一个 MyPlugin 类,并在其中实现了 pytest_runtest_makereport 钩子。我们还在 pytest_configure 和 pytest_unconfigure 函数中注册和注销插件。
通过这种方式,你可以轻松地扩展 pytest 的功能,自定义测试报告的行为。
4 pytest_sessionfinish(session, exitstatus) 钩子函数
pytest_sessionfinish 是 pytest 提供的一个钩子(hook),它允许你在整个测试会话结束时执行一些操作。这个钩子在所有测试项运行完毕后被调用,但在此之前所有测试收集、设置(setup)、执行以及清理(teardown)都已经完成。这意味着你可以利用这个钩子来做一些总结性的工作,比如输出一些统计信息,关闭资源,清理环境等。
这个钩子有两个参数:
session: 一个 Session 对象,提供了对当前测试会话的信息访问。
exitstatus: 一个整数,表示测试会话的退出状态码。这可以用来决定脚本的退出状态,例如是否成功执行。
如何使用 pytest_sessionfinish
下面是一个简单的例子来展示如何使用 pytest_sessionfinish 钩子:
import pytest
def pytest_sessionfinish(session, exitstatus):
在这里添加你的逻辑
print("All tests have finished.")
print(f"Exit status: {exitstatus}")
你也可以根据 exitstatus 决定做一些不同的事情
if exitstatus == 0:
print("All tests passed.")
else:
print("Some tests did not pass.")
更复杂的用法
如果你想要创建一个插件,可以像这样定义 pytest_sessionfinish 钩子:
import pytest
class MyPlugin:
def pytest_sessionfinish(self, session, exitstatus):
print("All tests have finished.")
print(f"Exit status: {exitstatus}")
def pytest_configure(config):
注册插件
config.pluginmanager.register(MyPlugin(), "my_plugin")
def pytest_unconfigure(config):
清理插件
plugin = getattr(config, "my_plugin", None)
if plugin is not None:
del config.my_plugin
config.pluginmanager.unregister(plugin)
Session 对象
session 参数是一个 pytest.Session 实例,它提供了许多有用的方法和属性来访问关于测试会话的信息。例如,你可以使用 session.items 获取测试项列表,session.testsfailed 获取失败的测试数量,以及其他有用的统计信息。
Exit Status
exitstatus 是一个整数,表示测试会话的退出状态。常见的值包括:
0: 所有测试通过。
1: 测试失败或命令行选项无效。
2: 用户中断(Ctrl+C)。
5: 内部错误。
你可以根据这个状态码来决定后续的动作,例如发送通知或更新数据库的状态。
通过使用 pytest_sessionfinish 钩子,你可以确保在测试会话结束后执行一些必要的清理工作或通知操作。
pytest+allure测试报告(图文详解)
0.入口python文件执行pytest程序,并生成报告展示
os.system('python -m pytest --alluredir ./allure --clean-alluredir')
time.sleep(3)
os.system('allure serve ./allure')
说明:
python -m pytest: -m使用pytest模块执行
--alluredir ./allure:将测试结果保存到 allure目录中.(或者使用 --alluredir=./allure),是pytest-allure-adaptor插件中的选项。
--clean-alluredir:在保存测试结果之前先清理 allure 目录,是插件pytest-allure-adaptor 中的选项。
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qadnkz/article/details/136333732
标签:一般



发表评论: