如果有以下场景:用例 1 需要先登录,用例 2 不需要登录,用例 3 需要先登录。很显然无法用 setup 和 teardown 来实现了
fixture 可以让我们自定义测试用例的前置条件
命名方式灵活,不局限于 setup 和 teardown 这几个命名
conftest.py 配置里可以实现数据共享,不需要 import 就能自动找到 fixture
scope 的取值:
function:每一个函数或方法都会调用
class:每一个类调用一次,一个类可以有多个方法
module:每一个.py 文件调用一次,该文件内又有多个 function 和 class
session:是多个文件调用一次,可以跨.py 文件调用,每个.py 文件就是 module
@pytest.fixture(scope="function", params=None, autouse=False, ids=None, name=None)
def test():
print("fixture初始化的参数列表")
scope:可以理解成 fixture 的作用域,默认:function,还有 class、module、package、session 四个【常用】
autouse:默认:False,需要用例手动调用该 fixture;如果是 True,所有作用域内的测试用例都会自动调用该 fixture
name:默认:装饰器的名称,同一模块的 fixture 相互调用建议写个不同的 name
注意 session 的作用域:是整个测试会话,即开始执行 pytest 到结束测试
@pytest.mark.usefixtures(fixture_name)
import pytest
# 调用方式一:将fixture名称作为测试用例函数的输入参数
@pytest.fixture
def login():
print("输入账号,密码先登录")
def test_s1(login):
print("用例 1:登录之后其它动作 111")
>>>====auto===
>>>输出账号,密码先登录
>>>用例 1:登录之后其它动作 111
def test_s2(): # 不传 login
print("用例 2:不需要登录,操作 222")
>>>====auto===
>>>用例 2:不需要登录,操作 222
# 调用方式二:测试用例加上装饰器:@pytest.mark.usefixtures(fixture_name)
@pytest.fixture
def login2():
print("please输入账号,密码先登录")
@user6res("login2", "login")
def test_s11():
print("用例 11:登录之后其它动作 111")
>>>====auto===
>>>please输入账号,密码先登录
>>>输入账号,密码先登录
>>>用例 11:登录之后其它动作 111
# 调用方式三:fixture设置autouse=True,有作用域内的测试用例都会自动调用该fixture
@pytest.fixture(autouse=True)
def login3():
print("====auto===")
# 不是test开头,加了装饰器也不会执行fixture
@user8res("login2")
def loginss():
print(123)
Point!!!
- 在类声明上面加
@pytest.mark.usefixtures()
,代表这个类里面所有测试用例都会调用该 fixture 可以叠加多个@pytest.mark.usefixtures()
,先执行的放底层,后执行的放上层 可以传多个 fixture 参数,先执行的放前面,后执行的放后面 如果 fixture 有返回值,用@pytest.mark.usefixtures()
是无法获取到返回值的,必须用传参的方式(方式一)
import pytest
order = []
@pytest.fixture(scope="session")
def s1():
order.append("s1")
@pytest.fixture(scope="module")
def m1():
order.append("m1")
@pytest.fixture
def f1(f3, a1):
# 先实例化f3, 再实例化a1, 最后实例化f1
order.append("f1")
assert f3 == 123
@pytest.fixture
def f3():
order.append("f3")
a = 123
yield a
@pytest.fixture
def a1():
order.append("a1")
@pytest.fixture
def f2():
order.append("f2")
def test_order(f1, m1, f2, s1):
# m1、s1在f1后,但因为scope范围大,所以会优先实例化
assert order == ["s1", "m1", "f3", "a1", "f1", "f2"]
首先看有没有 autouse=True,然后 f1,m1,f2,s1 中 scope 最大的是 s1,然后是 m1,所以第一个是” s1“, 第二个是” m1“,然后其他的 scope 相同,先看 f1,将 f1 依赖的 f3 先实例化,所以接下来是” f3“,然后是” a1“,然后回到 f1,order 新增” f1“,最后是” f2“
值得注意的是,f3 中用到了 yield,yield 首先是一个 return,将 123return 后在 f1 中断言成功
添加了 @pytest.fixture
,如果 fixture 还想依赖其他 fixture,需要用函数传参的方式,不能用 @pytest.mark.usefixtures()
的方式,否则会不生效
@pytest.fixture(scope="session")
def open():
print("===打开浏览器===")
@pytest.fixture
# @pytest.mark.usefixtures("open") 不可取!!!不生效!!!
def login(open):
# 方法级别前置操作setup
print(f"输入账号,密码先登录{open}")
用 fixture 实现 teardown 并不是一个独立的函数,而是用 yield 关键字来开启 teardown 操作
@pytest.fixture(scope="session")
def open_browser():
# 会话前置操作setup
print("===打开浏览器===")
test = "测试变量是否返回"
yield test
# 会话后置操作teardown
print("==关闭浏览器==")
@pytest.fixture
def login(open_browser):
# 方法级别前置操作setup
print(f"输入账号,密码先登录{open_browser}")
name = "==我是账号=="
pwd = "==我是密码=="
age = "==我是年龄=="
# 返回变量
yield name, pwd, age
# 方法级别后置操作teardown
print("登录成功")
def test_s1(login):
print("==用例1==")
# 返回的是一个元组
print(login)
# 分别赋值给不同变量
name, pwd, age = login
print(name, pwd, age)
assert "账号" in name
assert "密码" in pwd
assert "年龄" in age
def test_s2(login):
print("==用例2==")
print(login)
如果 yield 前面的代码,即 setup 部分已经抛出异常了,则不会执行 yield 后面的 teardown 内容
如果测试用例抛出异常,yield 后面的 teardown 内容还是会正常执行
@pytest.fixture(scope="module")
def smtp_connection():
with smtplib.SMTP("smtp.gmail.com", 587, timeout=5) as smtp_connection:
yield smtp_connection # provide the fixture value
上下文管理器:
有上下文管理器前:f = open("test.txt") try: for line in f.readlines(): print(line) finally: f.close()
有上下文管理器后:
with expression [as variable]: # 如果指定了as variable说明符,则variable是上下文管理器expression调用__enter__()函数返回的对象。所以,f并不一定就是expression,而是expression.__enter__()的返回值;with-block是执行语句,with-block执行完毕时,with语句会自动进行资源清理. with-block with open("text.txt") as f: for line in f.readlines() print(line)
所以上面 smtpObj = smtplib.SMTP( [host [, port [, local_hostname]]] ),创建 SMTP 对象,执行完模块中的用例后,则返回一个 SMTP 对象,可以发送邮件以及完成一些资源清理操作。