fixture的作用
1.同unittest的setup和teardown,作为测试前后的初始化设置。
fixture的使用
1.作为前置条件使用
2.fixture的的作用范围
1.作为前置条件使用
@pytest.fixture()
def a():
return 3
def test_b(a):
assert a==3
2.fixture的作用范围
首先实例化更高范围的fixture.默认为scope="function",每个测试函数都会执行一次。
- session : 多个.py文件执行时,只调用一次
- module: 每一个.py调用一次
- class : 每个类调用一次
- function: 每个函数调用一次
3.fixture 作为setup/teardown执行
yield 前的在测试之前执行,yield后的在测试完后执行
@pytest.fixture def a(): print("setup") yield print("teardown") def test_b(a): print("测试")
4.fixture with
# @pytest.fixture() # def write_file(): # f = open("myfile.txt","w") # f.write("hello") # yield # f.close() @pytest.fixture() def write_file(): with open("myfile.txt","w") as f: f.write("hello1") def test_write_file(write_file): print("ceshi")
5. fixture request可以请求测试的上下文
@pytest.fixture(params=[1,3]) def a(request): return request.param def test_b(a): assert a in [1,3]
6.fixture 工厂模式
在单个测试中,如果想多次调用该fixture,就可以用工厂模式。fixture的结果返回的不是数据,而是返回生成数据的函数。
@pytest.fixture def make_customer_record(): def _make_customer_record(name): return {"name": name, "orders": []} return _make_customer_record def test_customer_records(make_customer_record): customer_1 = make_customer_record("Lisa") customer_2 = make_customer_record("Mike") customer_3 = make_customer_record("Meredith")
7.带参数的fixture
params列表有几个值,测试就会执行几次。例params=[1,2],测试用例会执行2次
@pytest.fixture(params=[0, 1, pytest.param(2, marks=pytest.mark.skip)]) def data_set(request): return request.param def test_data(data_set): assert data_set in [0,1]
8. 模块化:在一个fixtrue调用另一个fixture
fixture a可以调用另一个fixture c
@pytest.fixture() def c(): print("c") @pytest.fixture() def a(c): print("a") def test_b(a): pass
9.通过fixture 对测试用例进行分组
pytest在测试运行期间最大程度地减少了fixture的数量。如果有参数化的fixtrue,那么使用它的所有测试将首先使用一个实例执行,然后在创建下一个fixture实例之前调用终结器。除其他外,这简化了对创建和使用全局状态的应用程序的测试。
@pytest.fixture(scope="module", params=[1, 2])
def input_x(request):
print("setup")
param = request.param
yield param
print("teardown")
def test_a(input_x): print("test_a") assert input_x in [1,2] def test_b(input_x): print("test_b") assert input_x in [1,2] if __name__ == '__main__': pytest.main(['-q','fixture_1.py']) 运行结果 fixture_1.py setup .test_a .test_b teardown setup .test_a .test_b teardown
如上所示,设置scope='module',会将所有调用该fixture的用例都执行一遍,而fixture只执行了一次。
10.使用类、模块、项目中的fixture
userfixtures,可以调用在其他模块定义好的fixture.
@pytest.mark.usefixtures("x") class TestCase: def test_1(self): print("test_1") def test_2(self): print("test_2") if __name__ == '__main__': pytest.main(['-q','test_2.py'])
a.usefixture,可以添加多个fixture
@pytest.mark.usefixtures("a", "b") def test(): pass
b.可以使用标记机制的通用功能在测试模块级别指定fixture
pytestmark = pytest.mark.usefixtures("a") def test_a(): def test_b():
c.可以将项目中所有测试所需的fixture放入ini文件中
#pytest.ini [pytest] usefixtures = x #test_case.py @pytest.mark.usefixtures def test_a(): pass
11.自动使用fixture
autouse=True
有时你可能希望自动调用fixture, 而无需显示声明函数参数或usefixtures装饰器。
import pytest class DB: def __init__(self): self.intransaction = [] def begin(self, name): self.intransaction.append(name) def rollback(self): self.intransaction.pop() @pytest.fixture(scope="module") def db(): return DB() class TestClass: @pytest.fixture(autouse=True) def transact(self, request, db): db.begin(request.function.__name__) yield db.rollback() def test_method1(self, db): assert db.intransaction == ["test_method1"] def test_method2(self, db): assert db.intransaction == ["test_method2"]
类级transact
夹具标记有autouse = true ,这意味着该类中的所有测试方法都将使用此夹具,而无需在测试函数签名或类级usefixtures
修饰器中声明它。
- autouse固定装置遵循
scope=
关键字参数:如果有autouse固定装置,scope='session'
则无论定义在何处,都只能运行一次。scope='class'
表示它将每节课运行一次,依此类推。 - 如果在测试模块中定义了自动使用夹具,则其所有测试功能都会自动使用它。
- 如果在conftest.py文件中定义了自动使用的固定装置,则其目录下所有测试模块中的所有测试都将调用固定装置。
- 最后,请谨慎使用:如果您在插件中定义了自动使用夹具,它将在安装该插件的所有项目中的所有测试中调用它。如果固定装置仅在某些设置下(例如在ini文件中)仍然可以工作,则这很有用。这样的全局夹具应始终快速确定它是否应该做任何工作,并避免其他昂贵的导入或计算。
请注意,上面的transact
灯具很可能是您希望在项目中不启用的灯具。做到这一点的规范方法是将事务处理定义放入conftest.py文件中,而无需使用autouse
:
# content of conftest.py @pytest.fixture def transact(request, db): db.begin() yield db.rollback()
然后例如通过声明需要让TestClass使用它:
@pytest.mark.usefixtures("transact") class TestClass: def test_method1(self): ...
该TestClass中的所有测试方法都将使用事务处理夹具,而模块中的其他测试类或功能将不使用它,除非它们也添加了transact
引用。
fixture覆盖
1.对于测试文件夹级别,具有相同名称的fixture可以被覆盖
tests/ __init__.py conftest.py # content of tests/conftest.py import pytest @pytest.fixture def username(): return 'username' test_something.py # content of tests/test_something.py def test_username(username): assert username == 'username' subfolder/ __init__.py conftest.py # content of tests/subfolder/conftest.py import pytest @pytest.fixture def username(username): return 'overridden-' + username test_something.py # content of tests/subfolder/test_something.py def test_username(username): assert username == 'overridden-username'
2.在测试模块中覆盖fixture
tests/ __init__.py conftest.py # content of tests/conftest.py import pytest @pytest.fixture def username(): return 'username' test_something.py # content of tests/test_something.py import pytest @pytest.fixture def username(username): return 'overridden-' + username def test_username(username): assert username == 'overridden-username' test_something_else.py # content of tests/test_something_else.py import pytest @pytest.fixture def username(username): return 'overridden-else-' + username def test_username(username): assert username == 'overridden-else-username'
3.在文件内覆盖
tests/ __init__.py conftest.py # content of tests/conftest.py import pytest @pytest.fixture def username(): return 'username' @pytest.fixture def other_username(username): return 'other-' + username test_something.py # content of tests/test_something.py import pytest @pytest.mark.parametrize('username', ['directly-overridden-username']) def test_username(username): assert username == 'directly-overridden-username' @pytest.mark.parametrize('username', ['directly-overridden-username-other']) def test_username_other(other_username): assert other_username == 'other-directly-overridden-username-other'
4.对于某些测试模块,参数化的夹具被非参数化的版本覆盖,而非参数化的夹具被参数化的版本覆盖。显然,测试文件夹级别也是如此。
tests/ __init__.py conftest.py # content of tests/conftest.py import pytest @pytest.fixture(params=['one', 'two', 'three']) def parametrized_username(request): return request.param @pytest.fixture def non_parametrized_username(request): return 'username' test_something.py # content of tests/test_something.py import pytest @pytest.fixture def parametrized_username(): return 'overridden-username' @pytest.fixture(params=['one', 'two', 'three']) def non_parametrized_username(request): return request.param def test_username(parametrized_username): assert parametrized_username == 'overridden-username' def test_parametrized_username(non_parametrized_username): assert non_parametrized_username in ['one', 'two', 'three'] test_something_else.py # content of tests/test_something_else.py def test_username(parametrized_username): assert parametrized_username in ['one', 'two', 'three'] def test_username(non_parametrized_username): assert non_parametrized_username == 'username'
实用技巧
1.conftest.py 共享fixture。如果定义fixture过多且需要多个地方调用,可将fixture放入conftest.py文件中,使用时不需要导入