117

我想在我的测试套件中的每个测试之前和之后运行额外的设置和拆卸检查。我查看了固定装置,但不确定它们是否是正确的方法。我需要在每次测试之前运行设置代码,并且需要在每次测试后运行拆卸检查。

我的用例是检查没有正确清理的代码:它会留下临时文件。在我的设置中,我将检查文件,在拆解中我还将检查文件。如果有额外的文件,我希望测试失败。

4

7 回答 7

159

py.test 固定装置是实现您的目的的技术上足够的方法。

您只需要像这样定义一个固定装置:

@pytest.fixture(autouse=True)
def run_around_tests():
    # Code that will run before your test, for example:
    files_before = # ... do something to check the existing files
    # A test function will be run at this point
    yield
    # Code that will run after your test, for example:
    files_after = # ... do something to check the existing files
    assert files_before == files_after

通过用 声明您的夹具autouse=True,它将为同一模块中定义的每个测试函数自动调用。

也就是说,有一个警告。在设置/拆卸时断言是一种有争议的做法。我的印象是 py.test 的主要作者不喜欢它(我也不喜欢它,所以这可能会影响我自己的看法),所以你在前进时可能会遇到一些问题或粗糙的边缘。

于 2014-03-25T15:12:39.200 回答
33

您可以使用fixturein oder 来实现您想要的。

import pytest

@pytest.fixture(autouse=True)
def run_before_and_after_tests(tmpdir):
    """Fixture to execute asserts before and after a test is run"""
    # Setup: fill with any logic you want

    yield # this is where the testing happens

    # Teardown : fill with any logic you want

详细说明

  1. @pytest.fixture(autouse=True)来自文档“有时,您可能希望在不显式声明函数参数或 usefixtures 装饰器的情况下自动调用固定装置。” 因此,每次执行测试时都会运行此夹具。

  2. # Setup: fill with any logic you want,此逻辑将在每个测试实际运行之前执行。在您的情况下,您可以添加将在实际测试之前执行的断言语句。

  3. yield,如评论中所示,这是进行测试的地方

  4. # Teardown : fill with any logic you want,此逻辑将在每次测试后执行。无论测试期间发生什么,都可以保证此逻辑运行。

注意:pytest执行测试时失败的测试和错误之间存在差异。失败表示测试以某种方式失败。错误表明您无法进行正确的测试。

考虑以下示例:

在运行测试之前断言失败 -> 错误

import pytest


@pytest.fixture(autouse=True)
def run_around_tests():
    assert False # This will generate an error when running tests
    yield
    assert True

def test():
    assert True

运行测试后断言失败 -> 错误

import pytest


@pytest.fixture(autouse=True)
def run_around_tests():
    assert True
    yield
    assert False

def test():
    assert True

测试失败 -> 失败

import pytest


@pytest.fixture(autouse=True)
def run_around_tests():
    assert True
    yield
    assert True

def test():
    assert Fail

测试通过 -> PASSED

import pytest


@pytest.fixture(autouse=True)
def run_around_tests():
    assert True
    yield
    assert True

def test():
    assert True
于 2020-07-07T22:11:28.963 回答
15

夹具正是您想要的。这就是它们的设计目的。

无论您使用pytest 样式的固定装置,还是设置拆卸(模块、类或方法级别)xUnit 样式的固定装置,都取决于环境和个人喜好。

根据您的描述,您似乎可以使用pytest autouse fixtures
或 xUnit 风格的函数级别setup_function()/teardown_function()

Pytest 完全覆盖了你。如此之多,以至于它可能是信息的消防水带。

于 2015-08-19T22:18:22.530 回答
11

您可以使用 Pytest 的模块级设置/拆卸装置。

这是链接

http://pytest.org/latest/xunit_setup.html

它的工作原理如下:

 def setup_module(module):
     """ setup any state specific to the execution of the given module."""

 def teardown_module(module):
     """ teardown any state that was previously setup with a setup_module
     method."""

 Test_Class():
        def test_01():
          #test 1 Code

它将setup_module在此测试之前和teardown_module测试完成之后调用。

您可以在每个测试脚本中包含此夹具,以便为每个测试运行它。

如果您想使用目录中所有测试通用的东西您可以使用包/目录级别的固定装置鼻子框架

http://pythontesting.net/framework/nose/nose-fixture-reference/#package

__init__.py包的文件中,您可以包括以下内容

     def setup_package():
       '''Set up your environment for test package'''

     def teardown_package():
        '''revert the state '''
于 2014-03-31T12:30:31.070 回答
6

You may use decorators but programatically, so you don't need to put the decorator in each method.

I'm assuming several things in next code:

The test methods are all named like: "testXXX()" The decorator is added to the same module where test methods are implemented.

def test1():
    print ("Testing hello world")

def test2():
    print ("Testing hello world 2")

#This is the decorator
class TestChecker(object):
    def __init__(self, testfn, *args, **kwargs):
        self.testfn = testfn

    def pretest(self):
        print ('precheck %s' % str(self.testfn))
    def posttest(self):
        print ('postcheck %s' % str(self.testfn))
    def __call__(self):
        self.pretest()
        self.testfn()
        self.posttest()


for fn in dir() :
    if fn.startswith('test'):
        locals()[fn] = TestChecker(locals()[fn])

Now if you call the test methods...

test1()
test2()

The output should be something like:

precheck <function test1 at 0x10078cc20>
Testing hello world
postcheck <function test1 at 0x10078cc20>
precheck <function test2 at 0x10078ccb0>
Testing hello world 2
postcheck <function test2 at 0x10078ccb0>

If you have test methods as class methods, the approach is also valid. For instance:

class TestClass(object):
    @classmethod
    def my_test(cls):
        print ("Testing from class method")

for fn in dir(TestClass) :
    if not fn.startswith('__'):
        setattr(TestClass, fn, TestChecker(getattr(TestClass, fn)))

The call to TestClass.my_test() will print:

precheck <bound method type.my_test of <class '__main__.TestClass'>>
Testing from class method 
postcheck <bound method type.my_test of <class '__main__.TestClass'>>
于 2014-03-25T13:30:17.063 回答
1

这是一个老问题,但我个人从文档中找到了另一种方法:使用pytest.ini文件:

[pytest]
usefixtures = my_setup_and_tear_down
import pytest

@pytest.fixture
def my_setup_and_tear_down():

    # SETUP
    # Write here the logic that you need for the setUp

    yield # this statement will let the tests execute

    # TEARDOWN 
    # Write here the logic that you need after each tests

关于 yield 语句以及它如何允许运行测试:这里

于 2021-10-27T14:07:52.697 回答
0

默认情况下,夹具具有scope=function. 所以,如果你只是使用一个定义,比如

@pytest.fixture
def fixture_func(self)

它默认为(scope='function').

因此,fixture 函数中的任何终结器都将在每次测试后被调用。

于 2018-05-05T23:10:33.213 回答