【问题标题】:Managing many session-scoped fixtures in pytest在 pytest 中管理许多会话范围的固定装置
【发布时间】:2016-03-08 13:07:03
【问题描述】:

我的 conftest.py 中有以下代码

import pytest

@pytest.fixture(scope="class")
def fix1():
   print("i am in fix1")
   a = 10
   return a

@pytest.fixture(scope="class")
def fix2():
   print("i am in fix2")
   b = 20
  return b

@pytest.fixture(scope="session",autouse=True)
def setup_session(request):
    tp = TestSetup(fix1)
    tp.setup()
    def teardown_session():
      tp.teardown()
    request.addfinalizer(teardown_session)

class TestSetup(object):
    def __init__(self, fix1, fix2):
        self.fix1 = fix1
        self.fix2 = fix2
    def setup(self):
        print("i am in setup")
        print(self.fix1)
   def teardown(self):
       print("I am in teardown")
       print(self.fix2)

   # py.test -s test1.py 
   =========== test session starts ===========
   platform linux2 -- Python 2.7.5, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
   rootdir: /tmp/test, inifile: 
   collected 2 items 

   test1.py i am in setup
   <function fix1 at 0x2948320>
   i am in test1
   .i am in test2
   .I am in teardown

当我使用 pytest 执行上述操作时,永远不会调用固定装置 fix1 和 fix2。在我的任何测试运行之前,我需要一种方法来调用 fix1 和 fix2 作为设置和拆卸的一部分。

我想要实现的是在我的任何测试运行之前,我需要创建一个设置,fix1 和 fix2 是设置一些东西的夹具。我想在我的任何测试运行之前调用这些夹具,一旦所有测试都运行,我调用 teardown 函数来拆除我的设置。

【问题讨论】:

  • 当你在setup_session 函数中使用fix1 时,你不是传递了fixture,而是传递了fixture 的工厂函数(def fix1(): ...)。要真正使用夹具,您必须将其声明为setup_session 函数的参数。但是,这将导致错误:ScopeMismatch: You tried to access the 'class' scoped fixture 'fix1' with a 'session' scoped request object,您能告诉我们您想要达到什么目标,以及为什么要这样做,因为在我看来,您是在尝试将另一种测试风格硬塞到 py.test 中。
  • @EzequielMuns 我想要实现的是在我的任何测试运行之前,我需要创建一个设置,fix1 和 fix2 是设置一些东西的夹具。我想在我的任何测试运行之前调用这些夹具,一旦所有测试都运行,我调用 teardown 函数来拆除我的设置。

标签: python pytest fixtures


【解决方案1】:

如果您希望它创建一组在每个会话中创建一次、在每个测试中重复使用然后被拆除的固定装置,那么 py.test 这样做的方式是:

import pytest

@pytest.fixture(scope="session")
def fix1(request):
    # setup code here
    print('creating fix1')
    a = 10
    def teardown_fix1():
        # teardown code here
        print('destroying fix1')
        a = None
    request.addfinalizer(teardown_fix1)
    return a

def testOne(fix1):
    print('in testOne')
    assert fix1 == 10

def testTwo(fix1):
    print('in testTwo')
    assert fix1 != 20

(您已经知道如何执行此操作,如您的代码所示,因此这里没有新内容)。

您可以拥有多个会话范围的固定装置,并且它们是会话范围的事实保证它们将被创建一次,然后在测试运行结束时被拆除。

没有必要有一个主设置函数来设置每个灯具,而另一个主设置函数会拆除它们,而是你可以用如图所示的终结器将它们分离成它们自己的小工厂函数。

编辑

但也许有很多看起来几乎相同的固定装置,并且您想重用一些代码。在这种情况下,您可以拥有一个管理固定装置的类并将其设为 pytest 固定装置。然后你声明其他依赖于管理器的 pytest 夹具,每个都返回一个特定的夹具:

import pytest

class FixtureManager:
    def __init__(self):
        self.fixtures = {}

    def setup(self):
        print('setup')
        self.fixtures['fix1'] = self.createFixture('fix1')
        self.fixtures['fix2'] = self.createFixture('fix2')
        self.fixtures['fix3'] = self.createFixture('fix3')

    def get(self, name):
        return self.fixtures[name]

    def teardown(self):
        print('teardown')
        for name in self.fixtures.keys():
            # whatever you need to do to delete it
            del self.fixtures[name]

    def createFixture(self, name):
        # whatever code you do to create it
        return 'Fixture with name %s' % name

@pytest.fixture(scope="session")
def fixman(request):
    fixman = FixtureManager()
    fixman.setup()
    request.addfinalizer(fixman.teardown)
    return fixman

@pytest.fixture
def fix1(fixman):
    return fixman.get('fix1')

@pytest.fixture
def fix2(fixman):
    return fixman.get('fix2')

def testOne(fix1):
    print('in testOne')
    assert fix1 == 'Fixture with name fix1'

def testTwo(fix2):
    print('in testTwo')
    assert fix2 == 'Fixture with name fix2'

当然,您可以取消创建fix1fix2 pytest 固定装置,而是通过将fixman 注入您的测试函数并在那里调用get 来获取这些固定装置。你来判断什么更有意义并产生最少的样板。

【讨论】:

  • 但是如果我有很多夹具,我必须为每个夹具添加拆卸,有没有办法在一个功能中拆卸所有夹具?
  • 如果拆解代码完全相同或易于参数化,您可以在所有夹具中重用终结器函数。如果您必须创建一个夹具管理器类,就像您尝试过的那样,请参阅我的编辑。
  • 非常感谢,您的建议对我帮助很大。
猜你喜欢
  • 2018-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-15
  • 1970-01-01
相关资源
最近更新 更多