简体   繁体   English

在 py.test 中的每个测试之前和之后运行代码?

[英]Run code before and after each test in py.test?

I want to run additional setup and teardown checks before and after each test in my test suite.我想在我的测试套件中的每个测试之前和之后运行额外的设置和拆卸检查。 I've looked at fixtures but not sure on whether they are the correct approach.我看过固定装置,但不确定它们是否是正确的方法。 I need to run the setup code prior to each test and I need to run the teardown checks after each test.我需要在每次测试之前运行设置代码,并且需要在每次测试之后运行拆卸检查。

My use-case is checking for code that doesn't cleanup correctly: it leaves temporary files.我的用例是检查没有正确清理的代码:它会留下临时文件。 In my setup, I will check the files and in the teardown I also check the files.在我的设置中,我将检查文件,在拆卸中我也会检查文件。 If there are extra files I want the test to fail.如果有额外的文件,我希望测试失败。

py.test fixtures are a technically adequate method to achieve your purpose. py.test fixtures 是一种技术上足够的方法来实现你的目的。

You just need to define a fixture like that:你只需要定义一个这样的夹具:

@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

By declaring your fixture with autouse=True , it will be automatically invoked for each test function defined in the same module.通过使用autouse=True声明您的装置,它将为同一模块中定义的每个测试函数自动调用。

That said, there is one caveat.也就是说,有一个警告。 Asserting at setup/teardown is a controversial practice.在设置/拆卸时断言是一种有争议的做法。 I'm under the impression that the py.test main authors do not like it (I do not like it either, so that may colour my own perception), so you might run into some problems or rough edges as you go forward.我的印象是 py.test 的主要作者不喜欢它(我也不喜欢它,所以这可能会影响我自己的看法),因此您在前进时可能会遇到一些问题或粗糙的边缘。

You can use a fixture in oder to achieve what you want.您可以使用fixture来实现您想要的。

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

Detailed Explanation详细说明

  1. @pytest.fixture(autouse=True) , from the docs : "Occasionally, you may want to have fixtures get invoked automatically without declaring a function argument explicitly or a usefixtures decorator." @pytest.fixture(autouse=True)来自文档“有时,您可能希望在不显式声明函数参数或 usefixtures 装饰器的情况下自动调用夹具。” Therefore, this fixture will run every time a test is executed.因此,每次执行测试时都会运行此夹具。

  2. # Setup: fill with any logic you want , this logic will be executed before every test is actually run. # Setup: fill with any logic you want ,此逻辑将在每个测试实际运行之前执行。 In your case, you can add your assert statements that will be executed before the actual test.在您的情况下,您可以添加将在实际测试之前执行的断言语句。

  3. yield , as indicated in the comment, this is where testing happens yield ,如评论中所示,这是进行测试的地方

  4. # Teardown : fill with any logic you want , this logic will be executed after every test. # Teardown : fill with any logic you want ,这个逻辑将在每次测试后执行。 This logic is guaranteed to run regardless of what happens during the tests.无论测试期间发生什么,此逻辑都保证运行。

Note: in pytest there is a difference between a failing test and an error while executing a test.注意:pytest ,失败的测试和执行测试时的错误是有区别的。 A Failure indicates that the test failed in some way.失败表示测试以某种方式失败。 An Error indicates that you couldn't get to the point of doing a proper test.错误表示您无法进行适当的测试。

Consider the following examples:考虑以下示例:

Assert fails before test is run -> ERROR在测试运行之前断言失败 -> 错误

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

Assert fails after test is run -> ERROR测试运行后断言失败 -> 错误

import pytest


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

def test():
    assert True

Test fails -> FAILED测试失败 -> 失败

import pytest


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

def test():
    assert Fail

Test passes -> PASSED测试通过 -> PASSED

import pytest


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

def test():
    assert True

Fixtures are exactly what you want.固定装置正是您想要的。 That's what they are designed for.这就是它们的设计目的。

Whether you use pytest style fixtures , or setup and teardown (module, class, or method level) xUnit style fixtures, depends on the circumstance and personal taste.无论您使用pytest 风格的装置,还是设置拆卸(模块、类或方法级别)xUnit 风格的装置,都取决于环境和个人品味。

From what you are describing, it seems like you could use pytest autouse fixtures .根据您的描述,您似乎可以使用pytest autouse fixtures
Or xUnit style function level setup_function()/teardown_function() .或者 xUnit 风格的函数级别setup_function()/teardown_function()

Pytest has you completely covered. Pytest 已完全涵盖您。 So much so that perhaps it's a fire hose of information.如此之多以至于它可能是信息的消防水带。

You can use Module level setup/teardown Fixtures of Pytest.您可以使用 Pytest 的模块级设置/拆卸装置。

Here's the Link这是链接

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

It Works as follows:它的工作原理如下:

 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

It will call setup_module before this test and teardown_module after test completes.它将在此测试之前调用setup_module并在测试完成后调用teardown_module

You can include this fixture in each test-script to run it for each test.您可以在每个测试脚本中包含此夹具,以便为每个测试运行它。

IF you want to use something that is common to all tests in a directory You can use package/directory level fixtures nose framework如果您想使用目录中所有测试通用的东西,您可以使用包/目录级夹具鼻子框架

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

In __init__.py file of the package you can include following在包的__init__.py文件中,您可以包含以下内容

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

     def teardown_package():
        '''revert the state '''

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.测试方法都命名为:“testXXX()” 装饰器被添加到实现测试方法的同一模块中。

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:TestClass.my_test()的调用将打印:

precheck <bound method type.my_test of <class '__main__.TestClass'>>
Testing from class method 
postcheck <bound method type.my_test of <class '__main__.TestClass'>>

Fixtures by default have scope=function .灯具默认具有scope=function So, if you just use a definition such as所以,如果你只是使用一个定义,比如

@pytest.fixture
def fixture_func(self)

It defaults to (scope='function') .它默认为(scope='function')

So any finalizes in the fixture function will be called after each test.因此,在每次测试后都会调用夹具函数中的任何终结。

Ref: 1. http://programeveryday.com/post/pytest-creating-and-using-fixtures-for-streamlined-testing/参考:1. http://programeveryday.com/post/pytest-creating-and-using-fixtures-for-streamlined-testing/

It is an old question but I personally found another way from the docs : Use the pytest.ini file :这是一个老问题,但我个人从文档中找到了另一种方法:使用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

About the yield statement and how it allows to run the test : HERE关于 yield 语句以及它如何允许运行测试: 这里

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM