目录
conftest.py 和 @pytest.fixture 结合使用实现全局的前后置应用
pytest介绍
pytest 是一个流行且高效的Python测试框架,它提供了丰富的功能和灵活的用法,使得编写和 运行测试用例变得简单而高效
pytest的优势:
1.pytest语法简单,容易上手
2.pytest 内置了丰富的断言库,可以轻松地进行测试结果的判断
3.pytest 支持参数化测试,允许使用不同的参数多次运行同一个测试函数
4.pytest 有着丰富的插件生态系统,可以通过插件扩展各种功能
5.pytest 允许跳过指定用例,对某些预期失败的case标记成失败,支持重复执行失败的case
安装
pip install pytest==8.3.2
#通过pip list 来查看是否有pytest,存在则说明安装成功/后序的命名打印出版本号也说明安装成功
pip list
pytest --version
在没有使用pytest框架时,需要在main函数中一个个的进行调用

安装了 pytest 框架后方法名前有直接运行标志

用例命名规则
1. 文件名必须以 test_ 开头或者 _test 结尾(不区分大小写)
2. 测试类必须以 Test 开头,并且不能有 __init__ 方法(区分大小写)
3. 测试方法必须以 test 开头(区分大小写)

通过pytest直接执行测试用例,但这里Python类中有init构造函数,这里会出现问题

由于 pytest 的测试收集机制,测试类中不可以定义 __init__ 方法。
pytest 采用自动发现机制 来收集测试用例。它会自动实例化测试类并调用其所有以 test 结尾的方法作为测试用例。
如果测试类中定义了 __init__ 方法,那么当 pytest 实例化该类时, __init__ 方法会被调用,这可能 会掩盖测试类的实际测试逻辑,并引入额外的副作用,影响测试结果的准确性。
对此为了解决这一问题,pytest使用 setUp() 和 tearDown() 方法、使用类属性、使用 fixture 函数等来进行解决(后序介绍)
pytest命令参数
pytest 提供了丰富的命令行选项来控制测试的执行

def test01():
print("test01")
def test02():
print("test02")
class TestCode:
def test03(self):
print("test03")
def Test04(self):
print("Test04")
打印详细信息 pytest -sv(可以连写)
指定文件/测试用例:
#指定文件:pytest 包名/文件名
pytest pytestCode/test_01.py
#指定测试用例: pytest 包名/文件名::类名::方法名
pytest pytestCode/test_01.py::TestCode::test03

pytest配置文件
当在使用pytest执行测试用例时,在带参数时,会发现写的命名很长,这时就可以将相关配置参数统一放到pytest的配置文件中
在当前项目下创建 pytest.ini 文件,该文件为 pytest 的配置文件
下面为常见的配置选项

[pytest]
addopts = -vs
testpaths = ./pytestCode
python_files = test_*.py
python_classes = Test*
python_functions = test*
;python.ini文件内容
在配置完成后,就可以直接使用pytest来执行测试用例的,无需再额外指定其他参数
pytest.ini 文件通常位于项目的根目录下。通过在 pytest.ini 中定义配置项,可以覆盖 pytest 的默认行为,以满足项目的需求
前后置
为了解决上面的,使用pytest框架,测试类中不可以添加init()方法,如何进行数据的初始化? 提出了前后置的方法来完成数据的初始化
在测试框架中,前后置是指在执行测试用例前和测试用例后执行一些额外的操作,这些操作可以用于 设置测试环境、准备测试数据等,以确保测试的可靠性
pytest 框架提供三种方法做前后置的操作:
- setup_method 和 teardown_method :这两个方法用于类中的每个测试方法的前置和后置操 作。
- setup_class 和 teardown_class :这两个方法用于整个测试类的前置和后置操作。
- fixture :fixture 提供了更灵活的控制和更强大的功能,是 pytest 推荐的方式来实现测试用例的前置和后置操作(后序介绍)
验证前两种
class TestCode:
def setup_method(self):
print("Setup: Before each test")
def teardown_method(self):
print("Teardown: After each test")
def test_example1(self):
print("Running test_example1")
def test_example2(self):
print("Running test_example2")

class TestCode:
def setup_class(self):
print("Setup: Before all test")
def teardown_class(self):
print("Teardown: After all test")
def test_example1(self):
print("Running test_example1")
def test_example2(self):
print("Running test_example2")

断言
断言( assert )是一种调试辅助工具,用于检查程序的状态是否符合预期。如果断言失败(即条件 为假),Python解释器将抛出一个 AssertionError 异常。断言通常用于检测程序中的逻辑错误。 pytest 允许你在 Python 测试中使用标准的 Python assert 语句来验证预期和值
语法:assert 条件, 错误信息
条件 :必须是一个布尔表达式。
错误信息 :当条件为假时显示的错误信息,可选。
#断言基本数据类型
a = 1
b = 2
assert a == b
#断言字符串
str = "hello"
assert "hello" == str
# 函数断言
def divide(a, b):
assert b != 0, "除数不能为0"
return a / b
# 正常情况
print(divide(10, 2)) # 输出 5.0
# 触发断言
print(divide(10, 0)) # 抛出 AssertionError: 除数不能为0
def test():
# 断言列表
expect_list = [1, 'apple', 3.14]
actual_list = [1, 'apple', 3.14]
# 断言元组
expect_tuple = (1, 'apple', 3.14)
actual_tuple = (1, 'apple', 3.14)
# 断言字典
expect_dict = {'name': 'Alice', 'age': 25}
actual_dict = {'name': 'Alice', 'age': 25}
# 断言集合
expect_set = {1, 2, 3, 'apple'}
actual_set = {1, 2, 3, 'apple'}
assert expect_list == actual_list
assert expect_tuple == actual_tuple
assert expect_dict == actual_dict
assert expect_set == actual_set
参数化
pytest中内置的 pytest.mark.parametrize 装饰器允许对测试函数的参数进行参数化。参数化通过定义设计参数和规则,使得设计过程更加灵活和可控。
#在测试用例上使用参数化---单参数/多参数
#注意:parametrize定义的参数名与测试方法中传的参数名必须严格一致
#元组中的每一个都会依次执行一遍测试方法
@pytest.mark.parametrize("username",["zhangsan","lisi","lucy","lily"])
def test_01(username):
print(f"{username} is running")
@pytest.mark.parametrize("age",[10,20,30,40])
def test_01(age):
print("age is:",age)
@pytest.mark.parametrize("username,age",[("zhangsan",30),("lisi",35),("lucy",20),("lily",18)])
def test_01(username,age):
print(f"{username} age is {age}")
#执行测试用例的个数,3+4+4=11个
#在类上使用参数化
@pytest.mark.parametrize("n,expected", [(1, 2), (3, 4)])
class TestClass:
def test_simple_case(self, n, expected):
assert n + 1 == expected
def test_weird_simple_case(self, n, expected):
assert (n * 1) + 1 == expected
要对模块中的所有测试进行参数化,你可以将 pytestmark 全局变量赋值(名称必须为pytestmark)
pytestmark = pytest.mark.parametrize("username,age",[("zhangsan",30),("lisi",35)])
class Test_aa:
def test_01(self,username,age):
print(f"{username} age is {age}")
def test_02(self,username,age):
print(f"{username} age is {age}")
class Test_bb:
def test_01(self,username,age):
print(f"{username} age is {age}")
def test_02(self,username,age):
print(f"{username} age is {age}")
#自定义参数化数据源
def data_provider():
return [1,2,3,4]
@pytest.mark.parametrize("a",data_provider())
def test_01(a):
print(a)
skip跳过
跳过指定的测试用例,下面为一些使用场景
- 调试时不想执行这个用例
- 标记无法在某些平台上执行的测试功能
- 在某些版本中执行,其他版本中跳过
使用方式:
1.添加装饰器 @pytest.mark.skip/ @pytest.mark.skipif
2.代码中添加跳过代码 pytest.skip(reason)
@pytest.mark.skip
def test_01():
print("代码未开发完")
assert True
@pytest.mark.skip(reason="代码没有实现")
def test_02():
assert False
def check_login():
return True
def test_03():
print("start")
#如果未登录,则跳过后序步骤
if not check_login():
pytest.skip("未登录")
print("end")
#根据特定系统进行运行,符合条件的就会跳过
print(sys.platform)
@pytest.mark.skipif(sys.platform=='darwin',reason="does not run on mac")
def test_04():
assert True
@pytest.mark.skipif(sys.platform=='win',reason="does not run on windows")
def test_05():
assert True
@pytest.mark.skipif(sys.version_info < (3,7),reason="requires Python 3.7 or higher")
def test_06():
assert True
还有个相似的为将测试用例标记为fail,装饰器为@pytest.mark.xfail
fixture
pytest 中的 fixture 是一种强大的机制,用于提供测试函数所需的资源或上下文。它可以用于 设置测试环境、准备数据等。
基础使用
#未使用fixture
def init():
print("初始化")
def test_07():
init()
print("test_07")
#使用fixture
@pytest.fixture
def init():
print("初始化")
#此处会先调用init方法
def test_08(init):
print("test_08")
未标记 fixture 方法的调用与 fixture 标记的方法调用完全不一样,前者需要在方法体中调用, 而后者可以将函数名作为参数进行调用
测试脚本中存在的很多重复的代码、公共的数据对象时,使用 fixture 最为合适
fixture嵌套
# 安排
@pytest.fixture
def first_entry():
return "a"
# 安排
@pytest.fixture
def order(first_entry):
return [first_entry]
def test_string(order):
# 行动
order.append("b")
# 断言
assert order == ["a", "b"]
请求多个fixture
class Fruit:
def __init__(self, name):
self.name = name
def __eq__(self, other):
return self.name == other.name
@pytest.fixture
def my_fruit():
return Fruit("apple")
@pytest.fixture
def fruit_basket(my_fruit):
return [Fruit("banana"), my_fruit]
def test_my_fruit_in_basket(my_fruit, fruit_basket):
assert my_fruit in fruit_basket
测试和 fixture 不仅限于一次请求单个 fixture ,它们可以请求任意多个
yield fixture
当我们运行测试时,我们希望确保它们能够自我清理,以便它们不会干扰其他测试。pytest中的 fixture 提供了一个非常有用拆卸系统,它允许我们为每 个 fixture 定义具体的清理步骤。
一旦 pytest 确定了 fixture 的线性顺序,它将运行每个 fixture 直到它返回或 yield ,然后 继续执行列表中的下一个 fixture 做同样的事情。
测试完成后, pytest 将逆向遍历 fixture 列表,对于每个 yield 的 fixture ,运行 yield 语句之后的代码。
与其他fixture不同的为
- return 被替换为 yield
- 该 fixture 的任何拆卸代码放置在 yield 之后
@pytest.fixture
def init_destroy():
print("初始化")
yield
print("销毁")
def test(init_destroy):
print("working")

执行步骤为,pytest命令使用后,先调用test方法中的init_destroy方法,然后执行到yield返回打印working,测试完成,返回到yield的位置,继续往后执行
#创建文件句柄与关闭文件
@pytest.fixture
def file_read():
print("打开文件句柄")
fo = open("test.txt", "r")
yield fo
print("关闭打开的文件")
fo.close()
def file_write():
print("打开文件句柄")
fo = open("test.txt","w",encoding="utf-8")
return fo
# yield fo
#
# print("关闭文件句柄")
# fo.close()
def test_file(file_write, file_read):
# 写入数据
w = file_write
w.write("测试数据")
w.close() # 写入后关闭文件句柄,以便读取
# 读取数据
r = file_read
str = r.read(10)
print("文件内容:", str)
带参数的fixture
基本语法:
pytest.fixture(scope='', params='', autouse='', ids='', name='')
参数解释:
scope 参数用于控制fixture的作用范围,决定了fixture的生命周期。可选值有:
◦ function (默认):每个测试函数都会调用一次fixture。
◦ class :在同一个测试类中共享这个fixture。
◦ module :在同一个测试模块中共享这个fixture。(一个文件里)
◦ session :整个测试会话中共享这个fixture。
• autouse 参数默认为 False 。如果设置为 True ,则每个测试函数都会自动调用该fixture,
无需显式传入
• params 参数用于参数化fixture,支持列表传入。每个参数值都会使fixture执行一次,类似于
for循环
• ids 参数与 params 配合使用,为每个参数化实例指定可读的标识符(给参数取名字)
• name 参数用于为fixture显式设置一个名称。如果使用了 name ,则在测试函数中需要使用这个
名称来引用 fixture (给fixture取名字)
- scope 默认为 function ,这里的 function 可以省略不写,当 scope="function" 时, 每个测试函数都会调用一次 fixture 。 scope="class" 时,在同一个测试类中, fixture 只会在类中的第一个测试函数开始前执行一次,并在类中的最后一个测试函数结束后执行清理。
- 当 scope="moudle" 、 scope="session" 时可用于实现全局的前后置应用,这里需要多个 文件的配合
conftest.py 和 @pytest.fixture 结合使用实现全局的前后置应用
@pytest.fixture 与 conftest.py 文件结合使用,可以实现在多个测试模块( .py )文件中共享前后置操作,这种结合的方式使得可以在整个测试项目中定义和维护通用的前后置逻辑,使测试代码更加模块化和可维护
规则:
- conftest.py 是一个单独存放的夹具配置文件,名称是固定的不能修改
- 可以在项目中的不同目录下创建多个 conftest.py 文件,每个 conftest.py 文件都会对其所在目录及其子目录下的测试模块生效
- 在不同模块的测试中需要用到 conftest.py 的前后置功能时,不需要做任何的import导入操作
- 作用:可以在不同的 .py 文件中使用同一个 fixture 函数
示例:scope="moudle" 实现全局的前后置应用


scope="session"

#通过 params 实现参数化
# 定义一个参数化的 fixture
@pytest.fixture(params=["a", "b"])
def data_provider(request):
return request.param
# 定义一个测试函数,它依赖于上面的参数化 fixture
def test_data(data_provider):
assert data_provider != None
print(f"Testing with data provider: {data_provider}")
parametrize 更适合简单场景,而 fixture 更适合需要动态数据和资源管理的复杂场景。
指定用例执行顺序
在使用pytest进行测试时,有时候我们需要按照特定的顺序来运行测试用例,尤其是在涉及到测试用例 之间的依赖关系时。pytest本身并不直接支持通过配置来改变测试用例的默认运行顺序
可以通过pytest-order控制测试用例的执行顺序
安装命令
pip install pytest-order==1.3.0
在用法上既可以用在测试类上,也可以用在测试方法上
@pytest.mark.order(1)
def test_one():
assert True
@pytest.mark.order(2)
def test_two():
assert True

注:使用python解释器来执行测试用例
![]()
只需在正常的pytest命令之前加上python -m既可
1179

被折叠的 条评论
为什么被折叠?



