欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

pytest session scoped fixtures--pytest 会话作用域的fixtures(测试夹具)

程序员文章站 2024-02-27 13:45:15
...

原文博客链接
《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