【问题标题】:Run code before and after each test in py.test?在 py.test 中的每个测试之前和之后运行代码?
【发布时间】:2014-05-02 21:17:49
【问题描述】:

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

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

【问题讨论】:

    标签: python testing pytest test-fixture


    【解决方案1】:

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

    你只需要像这样定义一个fixture:

    @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 声明您的fixture,它将为同一模块中定义的每个测试函数自动调用。

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

    【讨论】:

    • 阅读@AugustinLaville 评论的人的注意事项:答案已经过编辑,现在是最新的
    • 从 pytest 3.0 开始,正式支持产生内部固定装置。 docs.pytest.org/en/latest/yieldfixture.html
    • 断言不应该在固定装置内完成。这不是最佳做法。
    • 非常好的答案
    【解决方案2】:

    您可以使用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
    

    详细说明

    1. @pytest.fixture(autouse=True), from the docs: “有时,您可能希望在不显式声明函数参数或 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
    

    【讨论】:

      【解决方案3】:

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

      您是使用 pytest 样式的固定装置,还是 setupteardown(模块、类或方法级别)xUnit 样式的固定装置,取决于环境和个人品味。

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

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

      【讨论】:

        【解决方案4】:

        您可以使用 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

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

        如果你想使用一个目录中所有测试通用的东西你可以使用包/目录级别的fixturesnose框架

        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 '''
        

        【讨论】:

          【解决方案5】:

          您可以通过编程方式使用装饰器,因此您不需要将装饰器放在每个方法中。

          我在下一个代码中假设了几件事:

          测试方法都命名为:“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])
          

          现在如果你调用测试方法...

          test1()
          test2()
          

          输出应该是这样的:

          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>
          

          如果您将测试方法作为类方法,则该方法也是有效的。例如:

          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)))
          

          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'>>
          

          【讨论】:

          • 这看起来可能适用于免费功能。我也有类功能(虽然我试图摆脱所有的测试类)。
          • 它也适用于类方法,我已经更新了我的答案。
          • 有人成功地在一个有固定装置的测试函数上运行装饰器吗?
          【解决方案6】:

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

          @pytest.fixture
          def fixture_func(self)
          

          默认为(scope='function')

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

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

          【讨论】:

          • 参考链接已损坏。
          【解决方案7】:

          这是一个老问题,但我个人从docs 中找到了另一种方法: 使用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 语句以及它如何允许运行测试:HERE

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-05-30
            • 1970-01-01
            • 2013-01-24
            • 2017-05-02
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多