【问题标题】:How do I correctly setup and teardown for my pytest class with tests?如何通过测试正确设置和拆卸我的 pytest 类?
【发布时间】:2014-12-11 21:04:06
【问题描述】:

我正在使用 selenium 进行端到端测试,但我不知道如何使用 setup_classteardown_class 方法。

我需要在setup_class方法中设置浏览器,然后执行一系列定义为类方法的测试,最后在teardown_class方法中退出浏览器。

但从逻辑上讲,这似乎是一个糟糕的解决方案,因为实际上我的测试不适用于类,而是对象。我在每个测试方法中传递self 参数,所以我可以访问对象的变量:

class TestClass:
  
    def setup_class(cls):
        pass
        
    def test_buttons(self, data):
        # self.$attribute can be used, but not cls.$attribute?  
        pass
        
    def test_buttons2(self, data):
        # self.$attribute can be used, but not cls.$attribute?
        pass
        
    def teardown_class(cls):
        pass
    

为类创建浏览器实例似乎也不正确。应该为每个对象分别创建它,对吗?

那么,我需要使用__init____del__ 方法而不是setup_classteardown_class

【问题讨论】:

    标签: python class object pytest


    【解决方案1】:

    根据Fixture finalization / executing teardown code,当前设置和拆卸的最佳实践是使用yield而不是return

    import pytest
    
    @pytest.fixture()
    def resource():
        print("setup")
        yield "resource"
        print("teardown")
    
    class TestResource:
        def test_that_depends_on_resource(self, resource):
            print("testing {}".format(resource))
    

    运行结果

    $ py.test --capture=no pytest_yield.py
    === test session starts ===
    platform darwin -- Python 2.7.10, pytest-3.0.2, py-1.4.31, pluggy-0.3.1
    collected 1 items
    
    pytest_yield.py setup
    testing resource
    .teardown
    
    
    === 1 passed in 0.01 seconds ===
    

    另一种编写拆卸代码的方法是将request-context object 接受到您的fixture 函数中,并使用执行一次或多次拆卸的函数调用其request.addfinalizer 方法:

    import pytest
    
    @pytest.fixture()
    def resource(request):
        print("setup")
    
        def teardown():
            print("teardown")
        request.addfinalizer(teardown)
        
        return "resource"
    
    class TestResource:
        def test_that_depends_on_resource(self, resource):
            print("testing {}".format(resource))
    

    【讨论】:

    • 所以你把它复制到你需要资源的每个测试文件中?
    • 这不是课程设置,对吧?它会在类中的每个测试方法之前执行。
    • 在这种特殊情况下,它仅在用作测试方法中的参数时才会执行。例如test_that_depends_on_resource(self, resource) 中的 resource 参数
    • 请注意,您可以将夹具范围设置为 'class' 并将 autouse 设置为 true 以确保为每个类调用一次代码,而不必将其作为参数包含在任何测试调用中:`` ` pytest.fixture(scope="class", autouse=True) def resource(): print("setup") yield "resource" print("teardown") ``
    【解决方案2】:

    当你写“定义为类方法的测试”时,你真的是指类方法(接收它的作为第一个参数的方法吗? ) 还是只是常规方法(接收 instance 作为第一个参数的方法)?

    由于您的示例使用self 作为测试方法,我假设是后者,所以您只需使用setup_method 代替:

    class Test:
    
        def setup_method(self, test_method):
            # configure self.attribute
    
        def teardown_method(self, test_method):
            # tear down self.attribute
    
        def test_buttons(self):
            # use self.attribute for test
    

    测试方法实例被传递给setup_methodteardown_method,但如果您的设置/拆卸代码不需要知道测试上下文,则可以忽略它。更多信息可以在here找到。

    我还建议您熟悉 py.test 的 fixtures,因为它们是一个更强大的概念。

    【讨论】:

    • Fixtures 比类方法弱:它们不允许破坏不是由它们创建的对象(这通常是真正需要的)。除此之外,感谢您提供信息。
    • 在将代码库从 pytest 的 3.0.x 版本升级到 4.x 变体时,这让我大吃一惊。一些较旧的代码使用 setup_class 和需要现代化的模拟方法等。 setup_class(self, foo, bar) --> setup_method(self,function,foo,bar)
    【解决方案3】:

    这可能有助于http://docs.pytest.org/en/latest/xunit_setup.html

    在我的测试套件中,我将测试用例分组到类中。对于该类中所有测试用例所需的设置和拆卸,我使用setup_class(cls)teardown_class(cls) 类方法。

    对于每个测试用例所需的设置和拆卸,我使用setup_method(method)teardown_method(methods)

    例子:

    lh = <got log handler from logger module>
    
    class TestClass:
        @classmethod
        def setup_class(cls):
            lh.info("starting class: {} execution".format(cls.__name__))
    
        @classmethod
        def teardown_class(cls):
            lh.info("starting class: {} execution".format(cls.__name__))
    
        def setup_method(self, method):
            lh.info("starting execution of tc: {}".format(method.__name__))
    
        def teardown_method(self, method):
            lh.info("starting execution of tc: {}".format(method.__name__))
    
        def test_tc1(self):
            <tc_content>
            assert 
    
        def test_tc2(self):
            <tc_content>
            assert
    

    现在,当我运行测试时,当 TestClass 开始执行时,它会记录开始执行时间、结束执行时间以及方法的详细信息..

    您可以在各自的位置添加其他设置和拆卸步骤。

    希望对你有帮助!

    【讨论】:

    • 嗨@Kiran,setup_classsetup_method 有什么区别?
    • @imsrgadich 当您将测试用例组织成类时,_class 用于类的设置和拆卸步骤,_method 是每个测试的相应步骤案例方法。
    • 该死的......现在我明白了!被困在上面几个小时。所以,要正确看待事情。整个班级的&lt;setup/teardown&gt;_class。在这里,可以是设置到 DB 的链接或加载数据文件。然后,每个测试用例都可以有自己的设置,格式为&lt;setup/teardown&gt;_method。现在事情已经很清楚了。非常感谢!
    【解决方案4】:

    正如@Bruno 所建议的,使用 pytest 固定装置是另一种可用于测试类甚至简单测试功能的解决方案。 Here's an example testing python2.7 functions:

    import pytest
    
    @pytest.fixture(scope='function')
    def some_resource(request):
        stuff_i_setup = ["I setup"]
    
        def some_teardown():
            stuff_i_setup[0] += " ... but now I'm torn down..."
            print stuff_i_setup[0]
        request.addfinalizer(some_teardown)
    
        return stuff_i_setup[0]
    
    def test_1_that_needs_resource(some_resource):
        print some_resource + "... and now I'm testing things..."
    

    所以,运行 test_1... 会产生:

    I setup... and now I'm testing things...
    I setup ... but now I'm torn down...
    

    请注意,夹具中引用了stuff_i_setup,允许该对象为setuptorn down,用于与之交互的测试。您可以想象这对于持久性对象(例如假设的数据库或某些连接)很有用,必须在每次测试运行之前清除它们以保持它们隔离。

    【讨论】:

      【解决方案5】:

      如果您添加 @classmethod 装饰器,您的代码应该可以按预期工作。

      @classmethod 
      def setup_class(cls):
          "Runs once per class"
      
      @classmethod 
      def teardown_class(cls):
          "Runs at end of class"
      

      http://pythontesting.net/framework/pytest/pytest-xunit-style-fixtures/

      【讨论】:

      • 这几乎就是文档中出现的内容。我在文档中遇到的问题是我很难理解上下文:self 传统上被称为 self,而不是 cls,所以这对我来说似乎很奇怪,脱离了类本身的上下文。 Kiran(上图)提供了这种背景。
      • @Cognitiaclaeves "self 传统上被称为 self,而不是 cls" 是的,self 用于实例方法,其中第一个参数是具体的对象实例其中方法操作正在发生,而cls用于@classmethods,它们绑定到类而不是类的实例(即对象)。
      【解决方案6】:
      import pytest
      class Test:
          @pytest.fixture()
          def setUp(self):
              print("setup")
              yield "resource"
              print("teardown")
      
          def test_that_depends_on_resource(self, setUp):
              print("testing {}".format(setUp))
      

      为了运行:

      pytest nam_of_the_module.py -v 
      

      【讨论】:

        【解决方案7】:

        我不确定我是否在您的原始问题中了解了使用 Selenium 的细节,但如果您只是询问如何使用更经典的 setUp/tearDown 样式,Pytest 支持大多数单元测试功能,因此您可以做点什么喜欢:

        import unittest
        
        
        class TestHello(unittest.TestCase):
        
            def setUp(self):
                print('running setUp')
        
            def test_one(self):
                print('running test_one')
        
            def test_two(self):
                print('running test_two')
        
            def tearDown(self):
                print('running tearDown')
        

        产生:

        $ pytest -s -v
        ====================== test session starts =======================
        platform linux -- Python 3.8.2, pytest-6.2.4, py-1.10.0, pluggy-0.13.1 -- /gnu/store/nckjv3ccwdi6096j478gvns43ssbls2p-python-wrapper-3.8.2/bin/python
        cachedir: .pytest_cache
        hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('/tmp/test/.hypothesis/examples')
        rootdir: /tmp/test
        plugins: hypothesis-5.4.1
        collected 2 items                                                
        
        test_hw.py::TestHello::test_one running setUp
        running test_one
        running tearDown
        PASSED
        test_hw.py::TestHello::test_two running setUp
        running test_two
        running tearDown
        PASSED
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2022-11-29
          • 1970-01-01
          • 1970-01-01
          • 2013-12-28
          • 1970-01-01
          • 2022-10-19
          • 2017-04-01
          • 1970-01-01
          相关资源
          最近更新 更多