二、夹具
2.1 什么是fixtures
在测试中,fixture为测试 提供了一个定义好的、可靠的和一致的上下文。这可能包括环境(例如配置有已知参数的数据库)或内容(例如数据集)。
2.2 使用上的理解
- 它是一个高级装饰器,通过被@Fixture标记的的函数,都叫做夹具.
- 把使用@Fixture标记的函数名称,传到任意用例的参数列表里面,即可使用
- 在不修改用例函数内部代码的前提下,扩展其功能,可以给用例提供数据,增强功能, 和测试前,测试后环境的初始化和清理工作。
2.3 初始化和清理
- fixture 函数会在测试函数之前运行
- 如fixtrue 函数包含yield,那么代码会在yield 处停止,转而运行测试函数,
- 等测试函数执行完毕后,在回到fixtrue,继续执行yield 后面的代码。
- 可以将yield 前、当做setup,yield后,当做teardown
- 无论测试过程中发生了什么,yield后面的代码都会被执行。它的作用视为代码清理功能。
- 类似UnitTest 里面的setup 和teardwn
2.4 共享夹具
- conftest.py: 跨多个文件共享夹具
- 该文件用作为整个目录提供共享Fixtrue 的一种方式,可以跨多个文件共享夹具,并且在用例模块中无需导入它们,pytest 会自动会发现conftest.py 中的夹具。
- 您可以拥有多个包含测试的嵌套目录/包,并且每个目录都可以拥有conftest.py. 如果夹具出现重名,子级会覆盖父级中的夹具
- conftest.py ,只针对当前目录和子级目录有效,对它当前的父级目录,不起作用。
2.5 代码案例
2.5.1 fixtrue夹具入门案例
# ---------------------------------conftest.py------------------------------------
import pytest
@pytest.fixture
def some_data():
return 66
@pytest.fixture
def student_score():
return {'小明': 99, '小红': 100}
@pytest.fixture
def student_name():
return ['小明', '小红']
# ---------------------------------test_01_fixtures.py----------------------------
from pytest_assume.plugin import assume
def test_some(some_data):
assert some_data == 66
def test_student(student_score, student_name):
with assume: # 实际的数据长度是否跟预期长度相同
assert len(student_score) == 2, \
f"实际数据长度:{len(student_score)} 与预期数据长度:{2}不相同!!"
with assume: # 预期数据是否在实际数据内
assert '小红' in student_name, \
f"预期数据:{'小红'}没在实际数据中:{student_name}!!"
2.5.2 完整邮件案例
测试步骤:
-
step1: 创建发邮件用户
setp2: 创建收邮件用户
step3:编写收发邮件功能用例。验证发件人可以正常发邮件到收件人邮箱里的功能用例。
step4: 删除收件用户
step5: 删 除发件用户
说明:step1、step2 为提供资源,由夹具来实现、step4、step5为清理,由夹具实现。
# ---------------------------------mail.py----------------------------------------
from dataclasses import dataclass, field
from typing import List
class MailAdminClient: # 邮件管理后台
def create_user(self): # 创建用户
return MailUser()
def delete_user(self, user): # 删除用户,未完成
# do some cleanup
pass
@dataclass
class Email:
subject: str # 邮件主题
body: str # 邮件内容
# 邮件使用人
@dataclass
class MailUser:
# 收件箱
inbox: List[Email] = field(default_factory=list)
def send_email(self, email, other):
"""发送邮件
:param email: 邮件(包括主题、内容)
:param other: 收件人
:return: None
"""
other.inbox.append(email)
def clear_mailbox(self):
"""清空邮箱
:return:None
"""
self.inbox.clear()
# ---------------------------------conftest.py------------------------------------
@pytest.fixture
def mail_admin():
from btest.Day3.mail import MailAdminClient
return MailAdminClient()
@pytest.fixture
def sending_user(mail_admin):
user = mail_admin.create_user()
print('\n--sending_user创建发送用户-------\n')
yield user
print('\n--sending_user删除发送用户-------\n')
mail_admin.delete_user(user)
@pytest.fixture
def receiving_user(mail_admin):
user = mail_admin.create_user()
print('\n--receiving_user创建接受用户-------\n')
yield user
print('\n--receiving_user删除接受用户-------\n')
mail_admin.delete_user(user)
# ---------------------------------test_02_email.py------------------------------
from btest.Day3.mail import Email
# step1:执行sending_user 创建发件用户
# step2: 执行receiving_user 创建收件用户
# step3: 执行test_email_received 用例
# step4: 执行receiving_user删除接受用户
# step5: 执行sending_user删除发送用户
def test_email_received(sending_user, receiving_user):
print('\n--test_email_received执行测试-------\n')
# 构造邮件
email = Email(subject="Hey!", body="How's it going?")
# 发送者发送邮件给接受者
sending_user.send_email(email, receiving_user)
# 断言 邮件是否在接收人的邮箱中
assert email in receiving_user.inbox
# 说明:
# 同级别的夹具环境初始化是顺序、环境清理是逆序。
# 夹具yield 前面代码是按照参数顺序执行。环境初始化是顺序。
# 夹具yield 后面代码是按照参数逆序执行。环境清理是逆序。
2.5.3 夹具执行顺序
当 pytest 想要执行测试时,一旦它知道将执行哪些夹具,它就必须弄清楚它们将执行的顺序。为此,它考虑了 3 个因素:
- 范围 :首先执行更高范围的夹具 ,例如:顺序 session > package > moudle > class > method 。
说明: 不管夹具在用例参数列表中,如何排列,都想先执行更高范围夹具 - 依赖关系 : 同级别夹具,当一个夹具请求另一个夹具时,首先执行另一个夹具。
- 自动夹具 : 同级别,在非自动夹具前执行。
说明:标注为autouse=True的夹具,在同级别夹具中,
import pytest
@pytest.fixture(scope="session")
def order():
return []
@pytest.fixture
def func_A(order):
order.append("function_A")
@pytest.fixture
def func_B(func_A, order):
order.append("function_B")
@pytest.fixture(autouse=True) # 同级别自动夹具,最先执行。
def func_C(order):
order.append("function_C")
@pytest.fixture(scope="class")
def cls(order):
order.append("class")
@pytest.fixture(scope="module")
def mod(order):
order.append("module")
@pytest.fixture(scope="package")
def pack(order):
order.append("package")
@pytest.fixture(scope="session")
def sess(order):
order.append("session")
class TestClass:
def test_order(self, func_B, func_A, cls, mod, pack, sess, order):
assert order == ['session', 'package', 'module', 'class', 'function_C', 'function_A', 'function_B']
2.5.4 夹具综合案例
# ----------------------------test_01_xh.py----------------------------------
# ----------------------------test_02_xm.py----------------------------------
# step1 夹具(session-自动) :初始化测试环境 与清理测环境。
# step2 夹具(module-自动): 登陆与退出
# step3 夹具(function) : 函数级别提供输入信息 和 结束输入信息。
# step4 用例(2个模块(4个)用例) : 用例1学生查询成绩,用例2学生修改成绩
# 注意:自动化夹具的使用场景,用例跟夹具,没有数据上的依赖时,可以使用自动化夹具。
# 通过关键字表达式进行测试,执行用例名字中包含searh和edit的用例
# pytest.main(['-qs', '-k', 'search or edit', '.\\Day4\\'])
>>>>>>>>>>>>session:测试开始-环境初始化<<<<<<<<<<<<<<<<
--------module:btest.Day4.test_01_xh:登陆成功-------
***function:test_xh_search:开始输入信息***
小红查询成绩
.***function:test_xh_search:结束输入信息***
***function:test_xh_edit:开始输入信息***
小红修改成绩
.***function:test_xh_edit:结束输入信息***
--------module:btest.Day4.test_01_xh:退出成功-------
--------module:btest.Day4.test_02_xm:登陆成功-------
***function:test_xm_search:开始输入信息***
小明查询成绩
.***function:test_xm_search:结束输入信息***
***function:test_xm_edit:开始输入信息***
小明修改成绩
.***function:test_xm_edit:结束输入信息***
--------module:btest.Day4.test_02_xm:退出成功-------
>>>>>>>>>>>>session:测试完毕-环境清理<<<<<<<<<<<<<<<<<<
# ----------------------------test_03_student.py----------------------------------
# step1 夹具(session-自动) :初始化测试环境 与清理测环境。
# step2 夹具(module-自动): 登陆与退出。
# step3 夹具(class-手动) : 类级别提供: 输入学生信息 和清空学生信息。
# step4 夹具(function-自动) : 函数级别提供: 开始输入信息 和 结束输入信息。
# step5 用例(2个class) : 用例1(判断小红是否在学成成绩表中),用例2(判断小明是否大于90分)
# 通过关键字表达式进行测试,执行用例名字中包含student的用例
# pytest.main(['-qs', '-k', 'student', '.\\Day4\\'])
>>>>>>>>>>>>session:测试开始-环境初始化<<<<<<<<<<<<<<<<
--------module:btest.Day4.test_03_student:登陆成功-------
+++class:TestStudentName:输入学生信息+++
***function:test_name:开始输入信息***
.***function:test_name:结束输入信息***
+++class:TestStudentName:清空学生信息+++
+++class:TestStudentScore:输入学生信息+++
***function:test_score:开始输入信息***
.***function:test_score:结束输入信息***
+++class:TestStudentScore:清空学生信息+++
--------module:btest.Day4.test_03_student:退出成功-------
>>>>>>>>>>>>session:测试完毕-环境清理<<<<<<<<<<<<<<<<<<
夹具常用场景总结
-
session 级别夹具:整个测试用例中开头和结尾只运行一次或全局共享资源(全局参数关联)
-
module级别夹具 :每个模块都需要重新初始化和运行,或在一个模块中共享某个资源(模块内部参数关联)
-
class级别夹具: 每个class都需要重新初始化和运行, 在一个类中共享某个资源(某个类中参数关联)
-
function级别夹具:每个用例都需要重新初始化和运行,用例之间不共享资源(不进行参数关联)
更多案例,更多内容,请点击: https://edu.csdn.net/course/detail/37237