pytest session scoped fixtures--pytest 会话作用域的fixtures(测试夹具)
原文博客链接
在《pytest fixtures nuts and bolts》这边博客中,我提到你可以通过限定会话作用域,来使fixture在整个测试会话期间运行一次,并且可以在多个测试函数、类、模块中访问。
在接下来的博客中,我将使用一个简单的例子来演示如何再说实际中使用:
下边的表格是前一篇博客中用到的:
function | Run once per test |
---|---|
class | Run once per class of tests |
module | Run once per module |
session | Run once per session |
conftest.py–一个用于fixtures的独立文件
在函数、类、以及module的作用域下,将fixture的代码和测试放在同一个文件是说的通的。
但是在会话作用域下,这样是不合理的。
我们可以将fixture的代码放到conftest.py文件。这是一个pytest指定寻找的特殊名字的文件。
pytest的官方文档之处这是为本地插件使用的,我们也可以将其用于本地fixtures。可以通过pytest.org网站来了解conftest.py的相关规定。
会话作用域fixtures的简单例子
我认为直接看实际的代码是最清晰的。
我准备了4个文件:
- conftest.py
- 2 fixtures
- my_own_session_run_at_beginning, 一个会话作用域自动使用的fixture
- some_resource, 一个会话作用域的非自动使用的fixture
- test_alpha.py
- 2个简单的测试函数
- test_alpha_1, 没有命名的fixture
- test_alpha_2, 有一个命名的fixture-some_resource
- test_beta.py
- 和test_alpha.py类似,只是使用了基于unittest的测试
- test_gamma.py
- 和test_aplha.py类似,使用了基于类的测试。
import pytest
@pytest.fixture(scope="session", autouse=True)
def my_own_session_run_at_beginning(request):
print('\nIn my_own_session_run_at_beginning()')
def my_own_session_run_at_end():
print('In my_own_session_run_at_end()')
request.addfinalizer(my_own_session_run_at_end)
@pytest.fixture(scope="session")
def some_resource(request):
print('\nIn some_resource()')
def some_resource_fin():
print('\nIn some_resource_fin()')
request.addfinalizer(some_resource_fin)
test_alpha.py:
def test_alpha_1():
print('\nIn test_alpha_1()')
def test_alpha_2(some_resource):
print('\nIn test_alpha_2()')
test_beta.py:
import unittest
import pytest
class BetaTest(unittest.TestCase):
def test_unit_beta_1(self):
print('\nIn test_unit_beta_1()')
@pytest.mark.usefixtures('some_resource')
def test_unit_beta_2(self):
print('\nIn test_unit_beta_2()')
test_gamma.py:
class TestGamma:
def test_gamma_1(self):
print('\nIn test_gamma_1()')
def test_gamma_2(self, some_resource):
print('\nIn test_gamma_2()')
Output
**Run with pytest -s -v
============================= test session starts ==============================
platform darwin -- Python 2.7.5 -- py-1.4.20 -- pytest-2.5.2 -- /usr/bin/python
collecting ... collected 6 items
test_alpha.py:1: test_alpha_1
In my_own_session_run_at_beginning()
In test_alpha_1()
PASSED
test_alpha.py:4: test_alpha_2
In some_resource()
In test_alpha_2()
PASSED
test_beta.py:5: BetaTest.test_unit_beta_1
In test_unit_beta_1()
PASSED
test_beta.py:8: BetaTest.test_unit_beta_2
In test_unit_beta_2()
PASSED
test_gamma.py:2: TestGamma.test_gamma_1
In test_gamma_1()
PASSED
test_gamma.py:5: TestGamma.test_gamma_2
In test_gamma_2()
PASSED
In some_resource_fin()
In my_own_session_run_at_end()
=========================== 6 passed in 0.04 seconds ===========================
混合使用function,module和session作用域
首先看看我准备了什么:
- 一个函数作用域的fixture-‘resource_c’
- 一个模块作用域的fixture-‘resource_b’
- 一个会话作用域的fixture-‘resource_a’
所有的这些都能完美工作
在这类例子中,我加入了一些autouse.
conftest.py:
import pytest
@pytest.fixture(scope="session")
def resource_a(request):
print('In resource_a()')
def resource_a_fin():
print('\nIn resource_a_fin()')
request.addfinalizer(resource_a_fin)
@pytest.fixture(scope="module")
def resource_b(request, resource_a):
print('In resource_b()')
def resource_b_fin():
print('\nIn resource_b_fin()')
request.addfinalizer(resource_b_fin)
@pytest.fixture(scope="function")
def resource_c(request, resource_b):
print('In resource_c()')
def resource_c_fin():
print('\nIn resource_c_fin()')
request.addfinalizer(resource_c_fin)
# these are just some fun dividiers to make the output pretty
# completely unnecessary, I was just playing with autouse fixtures
@pytest.fixture(scope="function", autouse=True)
def divider_function(request):
print('\n --- function %s() start ---' % request.function.__name__)
def fin():
print(' --- function %s() done ---' % request.function.__name__)
request.addfinalizer(fin)
@pytest.fixture(scope="module", autouse=True)
def divider_module(request):
print('\n ------- module %s start ---------' % request.module.__name__)
def fin():
print(' ------- module %s done ---------' % request.module.__name__)
request.addfinalizer(fin)
@pytest.fixture(scope="session", autouse=True)
def divider_session(request):
print('\n----------- session start ---------------')
def fin():
print('----------- session done ---------------')
request.addfinalizer(fin)
test_one_two.py:
def test_one(resource_c):
print('In test_one()')
def test_two(resource_c):
print('\nIn test_two()')
test_three_four.py:
def test_three(resource_c):
print('\nIn test_three()')
def test_four(resource_c):
print('\nIn test_four()')
**This seems reasonable to me.
What do you think will happen?**
output:
$ py.test -s -v
==================================== test session starts ====================================
platform darwin -- Python 2.7.5 -- py-1.4.20 -- pytest-2.5.2 -- /usr/bin/python
collected 4 items
test_one_two.py:1: test_one
----------- session start ---------------
------- module test_one_two start ---------
--- function test_one() start ---
In resource_a()
In resource_b()
In resource_c()
In test_one()
PASSED
In resource_c_fin()
--- function test_one() done ---
test_one_two.py:4: test_two
--- function test_two() start ---
In resource_c()
In test_two()
PASSED
In resource_c_fin()
--- function test_two() done ---
In resource_b_fin()
------- module test_one_two done ---------
test_three_four.py:1: test_three
------- module test_three_four start ---------
--- function test_three() start ---
In resource_b()
In resource_c()
In test_three()
PASSED
In resource_c_fin()
--- function test_three() done ---
test_three_four.py:4: test_four
--- function test_four() start ---
In resource_c()
In test_four()
PASSED
In resource_c_fin()
--- function test_four() done ---
In resource_b_fin()
------- module test_three_four done ---------
In resource_a_fin()
----------- session done ---------------
================================= 4 passed in 0.02 seconds ==================================
警告:应该按照越来越大的作用域使用
如果使用的顺序反了,情况会脱离控制。
我们尝试在几个测试上调整一下顺序
conftest.py:
...
@pytest.fixture(scope="module") # session -> module
def resource_a(request):
print('In resource_a()')
def resource_a_fin():
print('\nIn resource_a_fin()')
request.addfinalizer(resource_a_fin)
@pytest.fixture(scope="session") # module -> session
def resource_b(request, resource_a):
...
我们会在标准输出里面看到下述的错误和警告:
E ScopeMismatchError: You tried to access the 'module' scoped fixture 'resource_a'
... with a 'session' scoped request object, involved factories
E conftest.py:18: def resource_c(request, resource_b)
E conftest.py:10: def resource_b(request, resource_a)
E conftest.py:2: def resource_a(request)
所以,我们不能这么做。
警告同样适用于内嵌的fixtures。
pytest包括一些内嵌的fixtures。我相信他们都是function作用域。
这就意味着你只能将他们用在function作用域的fixtures。
我们深入一步讨论
我演示的代码只在fixture开始和结尾运行。
然而,你可以在session fixture的帮助下做更多,在pytest.org有一个很不错的例子A session-fixture which can look at all collected tests