测试前准备---执行测试--测试后清理
在pytest中也可以使用setup teardown进行测试前准备或测试后清理,如setp_method、setup_class、setup_module及对应的teardown_method、teardown_class、teardown_module
但是存在缺陷.
例如,在同一个测试类中,存在多个测试方法,假设每一个测试方法需要不同的 setup 或者 teardown 函数,此时该怎么办呢?
又比如,setup 和 teardown 其实都属于测试夹具(Test Fixtures),如果我想把所有测试夹具全部放到一个函数中去管理,能做到吗?
pytest 考虑到了这种情况,并且提供了一个更加高级的功能,那就是 fixture 装饰器。
fixtures 可用作初始化测试服务、数据和状态,也常常用来在测试执行前或测试执行后进行测试的前置操作或后置操作。
fixtures 可作为共享数据使用,也可被其他函数、模块、类或者整个项目,甚至另外的 fixtures 调用。
pytest.fixtures 的语法如下:
fixture(scope="function", params=None, autouse=False, ids=None, name=None)
从语法可以看到 fixture 的5个参数如下:
scope:用于控制 fixture 的作用范围
这个参数有以下4个级别:
function:在每一个 function 或者类方法中都会调用(默认)。
class:在每一个类中只调用一次。
module:每一个 .py 文件调用一次;该文件内可以有多个 function 和 class。
session:一个 session 调用一次。
params:一个可选的参数列表
params 以可选的参数列表形式存在。在测试函数中使用时,可通过 request.param 接收设置的返回值(即 params 列表里的值)。params 中有多少元素,在测试时,引用此 fixture 的函数就会调用几次。
autouse:是否自动执行设置的 fixtures
当 autouse 为 True 时,测试函数即使不调用 fixture 装饰器,定义的 fixture 函数也会被执行。
ids:指定每个字符串 id
当有多个 params 时,针对每一个 param,可以指定 id,这个 id 将变为测试用例名字的一部分。如果没有提供 id,则 id 将自动生成。
name:fixture 的名称
name 是 fixtures 的名称, 它默认是你装饰的那个 fixture 函数的名称。你可以通过 name 参数来更改这个 fixture 名称,更改后,如果这个 fixture 被调用,则使用你更改过的名称即可。
参考原文链接:https://blog.csdn.net/weixin_41754309/article/details/113185895
fixture的用法:
1、通过 fixture 函数名直接使用
import pytest
# 首先, 在fixture函数上,加@pytest.fixture()
@pytest.fixture()
def my_method():
print('测试前准备')
# 其次,把fixture函数的函数名作为参数,传入被测试用例
def test_use_fixtures(my_method):
print('这里是测试逻辑')
执行结果:

2.通过pytest.mark.usefixtures使用
import pytest
@pytest.fixture()
def my_method():
print('测试前准备')
# 函数直接使用fixture
@pytest.mark.usefixtures('my_method')
def test_use_fixtures():
print('测试函数使用fixture')
class TestClass1:
# 类方法使用fixture
@pytest.mark.usefixtures('my_method')
def test_class_method_usage(self):
print('测试类中方法使用fixture')
# 类直接使用fixture
@pytest.mark.usefixtures('my_method')
class TestClass2:
def test_method_usage_01(self):
print('1测试类直接使用fixture')
def test_method_usage_02(self):
print('2测试类直接使用fixture')
执行结果:

如果fixture有返回值,那么usefixture就无法获取到返回值,这个是装饰器usefixture与用例直接传fixture参数的区别。
当fixture需要用到return出来的参数时,只能讲参数名称直接当参数传入,不需要用到return出来的参数时,两种方式都可以。
3.fixture多参数
import pytest
@pytest.fixture(params=['hello', 'World'])
def my_method(request):
return request.param
def test_use_fixtures_01(my_method):
print('\n this is the 1st test')
print(my_method)
@pytest.mark.usefixtures('my_method')
def test_use_fixtures_02():
print('\n this is the 2nd test')
print(my_method)
#如果在这里想通过print(my_mthod)来打印出fixuture提供的参数,是不行的, 因为使用usefixtures无法获取fixture的返回值,如需要fixture的返回值,则需用test_use_fixtures_01那样的调用方式
执行结果:

4.通过 autouse 参数隐式使用
autouse默认是False,不调用fixture
import pytest
@pytest.fixture(params=['hello', 'World'], ids=['test1', 'test2'], name='test')
def my_method(request):
print(request.param)
def test_use_fixtures_01():
print('\n this is the 1st test')
def test_use_fixtures_02():
print('\n this is the 2nd test')
执行结果:

autouse=True允许我们在不调用 fixture 装饰器的情况下使用定义的fixture,如下:
import pytest
@pytest.fixture(params=['hello', 'World'], autouse=True, ids=['test1', 'test2'], name='test')
def my_method(request):
print(request.param)
def test_use_fixtures_01():
print('\n this is the 1st test')
def test_use_fixtures_02():
print('\n this is the 2nd test')
执行结果:

5.多 fixture 笛卡尔积使用
import pytest
class TestClass:
@pytest.fixture(params=['hello', 'Tester'], autouse=True)
def my_method1(self, request):
print('\nthe param are:{}'.format(request.param))
return request.param
@pytest.fixture(params=['VIPTEST', 'is good'], autouse=True)
def my_method2(self, request):
print('\nthe param are:{}'.format(request.param))
return request.param
def test_use_fixtures_01(self):
print("执行测试函数")
执行结果:

6.在日常工作测试中,常常需要在全局范围内使用同一个测试前置操作。例如,测试开始时首先进行登录操作,然后连接数据库。
在 conftest.py 中定义的 fixture 不需要进行 import,pytest 会自动查找使用。
pytest 查找 fixture 的顺序是首先查找测试类(Class),
接着查找测试模块(Module),
然后是 conftest.py 文件,
最后是内置或者第三方插件
conftest.py 定义在项目根目录,就可以应用在全局,定义在某一个文件夹,就可以应用于这个文件夹下的所有测试文件
如以下工程目录

conftest.py文件的fixtures如下
import pytest
@pytest.fixture(scope="session")
# 此方法名可以是登录的业务代码,也可以是其他,这里暂命名为login
def login():
print("登录。。。")
yield
print('测试完毕')
@pytest.fixture(scope="function", autouse=True)
def connect_db():
# 此方法名可以是连接数据库代码,也可以是其他,这里暂命名为login
print('connecting db')
yield
print('disconnecting db')
test_fixture1.py如下:
import pytest
class TestClass:
def test_use_fixtures_01(self, login):
print('\nI am test1:{}'.format(login))
def test_use_fixtures_02(self, login):
print('\nI am test2:{}'.format(login))
执行结果如下:

test_fixture2.py如下:
import pytest
class TestBaidu:
@pytest.mark.parametrize('search_string, expect_string', [('aaaaa', 'aaaaa'), ('bbbbb', 'bbbbb')])
def test_search(self, login, search_string, expect_string):
real_result = search_string
print(search_string)
assert real_result == expect_string
def test_fixture3(self,login):
print("执行fixture3")
if __name__ == "__main__":
pytest.main([])
执行结果如下:
