如何为带有测试的pytest类正确设置和拆卸?


103

我正在使用硒进行端到端测试,但无法获得使用方法setup_classteardown_class方法。

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

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

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__and __del__方法代替setup_classand teardown_class

Answers:


94

根据Fixture的完成/执行拆卸代码,当前设置和拆卸的最佳做法是使用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对象接受到您的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))

因此,将其复制到将需要资源的每个测试文件中吗?
安迪·海登

@AndyHayden取决于你如何写你的灯具,你可以把它在你需要它的每一个测试文件,或者你可以把它放在一个conftest.py文件stackoverflow.com/questions/34466027/...
埃弗雷特托斯

2
但是,这不是课程设置,对吗?它会在类中的每个测试方法之前执行。
malhar

1
在这种情况下,仅当在测试方法中用作参数时才执行。例如,resource参数test_that_depends_on_resource(self, resource)
Everett Toews

64

当您编写“定义为类方法的测试”时,您是说类方法(将其作为第一个参数的方法)还是常规方法(将实例作为第一个参数的方法)?

由于您的示例使用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,但是如果您的设置/拆卸代码不需要了解测试上下文,则可以忽略该方法。可以在这里找到更多信息

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


1
夹具比类方法弱:它们不允许破坏不是由它们创建的对象(通常这是真正必要的)。除此之外,感谢您提供信息。
wvxvw

这在将代码库从pytest的3.0.x版本升级到4.x变体时引起了我的兴趣。一些用于setup_class模拟方法的旧代码等需要进行现代化处理。setup_class(self, foo, bar)->setup_method(self,function,foo,bar)
jxramos

28

这可能会有所帮助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_classvs和有setup_method什么不一样?
imsrgadich

1
@imsrgadich将测试用例组织到类中时,<setup / teardown> _class用于类的设置和拆卸步骤,而<setup / teardown> _method是每种测试用例方法的相应步骤。
Kiran Vemuri

1
该死的...现在我明白了!被困了几个小时。因此,要把事情放在透视中。在<setup/teardown>_class全班。在这里,可以设置链接到数据库或加载数据文件之类的东西。然后,每个测试用例可以以的形式进行自己的设置<setup/teardown>_method。现在情况已经很清楚了。非常感谢!
imsrgadich

24

正如@Bruno所建议的那样,使用pytest固定装置是另一种解决方案,可用于两个测试类甚至是简单的测试函数。这是测试python2.7函数的示例

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为测试它与交互。您可以想象这对于持久性对象(例如假设的数据库或某些连接)很有用,必须在每次测试运行之前清除这些持久性对象以使它们隔离。


13

添加@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

1
@Cognitiaclaeves “传统上将self称为self,而不是cls”是,self用于实例方法,其中第一个参数是对其进行方法操作的特定对象实例,而cls用于@classmethods则绑定到类而不是类的实例(即对象)。
–'code_dredd
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.