Pytest-Fixture妙用
1 每次测试可以多次请求fixture(缓存返回值)
原文:Fixtures can also be requested more than once during the same test, and pytest won’t execute them again for that test.
This means we can request fixtures in multiple fixtures that are dependent on them (and even again in the test itself)
without those fixtures being executed more than once.
意思就是在一个test用例中可以对同一个fixture进行多次请求,pytest仅会执行一次,下面看个例子。
1 import pytest 2 3 4 # Arrange 5 @pytest.fixture 6 def first_entry(): 7 return "a" 8 9 10 # Arrange 11 @pytest.fixture 12 def order(): 13 return [] 14 15 16 # Act 17 @pytest.fixture 18 def append_first(order, first_entry): 19 return order.append(first_entry) 20 21 22 def test_string_only(append_first, order, first_entry): 23 # Assert 24 assert order == [first_entry]
如果在一个 test 用例中每请求一次fixture时,都将该 fixture 执行一遍,那么这个测试会失败。因为 append_first 和 test_string_only 都会将 order 视为一个空列表,
但是 pytest 会在第一次访问 order 时缓存 order 的返回值,在整个 test 用例和 append_first 中都引用相同的 order 对象,因此 append_first 里面是对该 order 对象进行的操作。
可以用 --setup-show 参数观察 fixture 的访问过程,可以看到 order 仅被执行一次
2 直接添加终结器addfinalizer
yield与addfinalizer的区别
1. addfinalizer可以注册多个终结函数,但是要注意的是执行顺序,与注册的顺序相反。
2. 当setUp的代码执行错误,addfinalizer依旧会执行,而yield是不会执行teardown代码的。
1 @pytest.fixture 2 def receiving_user(mail_admin, request): 3 user = mail_admin.create_user() 4 5 def delete_user(): 6 mail_admin.delete_user(user) 7 8 request.addfinalizer(delete_user) 9 return user
3 Fixture 可以内省访问测试上下文
fixture 函数可以接受 request 对象内省“请求”测试函数、类或模块上下文
例:从使用fixture的测试模块中读取一个可选的服务器URL
1 # content of conftest.py 2 import pytest 3 import smtplib 4 5 6 @pytest.fixture(scope="module") 7 def smtp_connection(request): 8 server = getattr(request.module, "smtpserver","smtp.gmail.com") 9 smtp_connection = smtplib.SMTP(server, 587, timeout=5) 10 yield smtp_connection 11 print("finalizing {} ({})".format(smtp_connection, server)) 12 smtp_connection.close()
1 # content of test_anothersmtp.py 2 3 smtpserver = "mail.python.org" # will be read by smtp fixture 4 5 6 def test_showhelo(smtp_connection): 7 assert 0, smtp_connection.helo()
可以观察到,一般是先执行 conftest 中的 fixture,然后再执行测试文件 test_anothersmtp.py,但是该 fixture 中可以内省访问测试文件中的 smtpserver 属性。
4 使用marker将数据传输到fixture
使用 request
对象时,fixture还可以访问应用于测试函数的标记marker。这对于将数据从测试传递到 fixture 中非常有用
1 import pytest 2 3 4 @pytest.fixture 5 def fixt(request): 6 marker = request.node.get_closest_marker("fixt_data") 7 if marker is None: 8 # Handle missing marker in some way... 9 data = None 10 else: 11 data = marker.args[0] 12 13 # Do something with the data 14 return data 15 16 17 @pytest.mark.fixt_data(43) 18 def test_fixt(fixt): 19 assert fixt == 43 20 21 if __name__ == '__main__': 22 pytest.main(['-v', 'test_fixture.py'])
5 对参数化fixture使用marks
pytest.param() 可用于在参数化 fixture 的值集中应用标记,方法与它们可用于的方法相同 @pytest.mark.parametrize
1 # content of test_fixture_marks.py 2 import pytest 3 4 5 @pytest.fixture(params=[0, 1, pytest.param(2,marks=pytest.mark.skip)]) 6 def data_set(request): 7 return request.param 8 9 10 def test_data(data_set): 11 pass
运行此测试将跳过 参数值为2 的调用
$ pytest test_fixture_marks.py -v =========================== test session starts ============================ platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collecting ... collected 3 items test_fixture_marks.py::test_data[0] PASSED [ 33%] test_fixture_marks.py::test_data[1] PASSED [ 66%] test_fixture_marks.py::test_data[2] SKIPPED (unconditional skip) [100%] ======================= 2 passed, 1 skipped in 0.12s =======================