如何通过测试正确设置和拆除我的pytest类?

ava*_*sin 73 python class object pytest python-2.7

我正在使用selenium进行端到端测试,我无法获得如何使用setup_classteardown_class方法.

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

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

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
Run Code Online (Sandbox Code Playgroud)

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

所以,我需要使用__init____del__方法而不是setup_classteardown_class

Eve*_*ews 68

根据Fixture定型/执行拆解代码的使用addfinalizer是"历史".

作为历史记录,另一种编写拆卸代码的方法是将一个请求对象接受到fixture函数中,并可以调用其request.addfinalizer一次或多次:

目前安装和拆卸的最佳实践是使用 yield

import pytest

@pytest.fixture()
def resource():
    print("setup")
    yield "resource"
    print("teardown")

class TestResource(object):
    def test_that_depends_on_resource(self, resource):
        print("testing {}".format(resource))
Run Code Online (Sandbox Code Playgroud)

运行它会导致

$ 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 ===
Run Code Online (Sandbox Code Playgroud)

  • 那么您将其复制到您需要该资源的每个测试文件中吗? (3认同)
  • 但是,这不是课程设置,对吗?它将在类中的每个测试方法之前执行。 (2认同)
  • 请注意,您可以将固定范围设置为“类”并将自动使用设置为 true,以确保为每个类调用一次代码,而不必将其作为参数包含在任何测试调用中:``` pytest.fixture(scope=类”,autouse = True)def资源():打印(“设置”)产量“资源”打印(“拆卸”)``` (2认同)

Bru*_*ira 47

当你编写"定义为类方法的测试"时,你真的是指类方法(接收其作为第一个参数的方法)还是常规方法(接收实例作为第一个参数的方法)?

由于您的示例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
Run Code Online (Sandbox Code Playgroud)

测试方法实例传递给setup_methodteardown_method,但如果您的setup/teardown代码不需要知道测试上下文,则可以忽略.更多信息可以在这里找到.

我还建议你熟悉py.test的灯具,因为它们是一个更强大的概念.

  • @MartinThoma - 你想在python2中使用`class Test(object)`这是绝对正确的.在python3中,它是多余的,因为默认情况下所有新类都继承自`object`.在python3代码中编写它仍然可以,因为它不会破坏/更改任何东西(而显式优于隐式... :) (5认同)
  • Fixtures 比类方法弱:它们不允许销毁不是由它们创建的对象(这通常是真正必要的)。除此之外,谢谢你的信息。 (2认同)

eco*_*coe 24

正如@Bruno建议的那样,使用pytest fixture是另一种解决方案,可以访问两个测试类甚至只是简单的测试函数.这是一个测试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..."
Run Code Online (Sandbox Code Playgroud)

所以,跑步test_1...产生:

I setup... and now I'm testing things...
I setup ... but now I'm torn down...
Run Code Online (Sandbox Code Playgroud)

请注意,stuff_i_setup在fixture中引用了该对象,setup并允许该对象进行torn down与之交互的测试.您可以想象这对于持久对象(例如假设数据库或某些连接)非常有用,必须在每次测试运行之前将其清除以保持隔离.


Kir*_*uri 17

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

在我的测试套件中,我将测试用例分组到类中.对于我需要该类中所有测试用例的设置和拆解,我使用了setup_class(cls)teardown_class(cls)classmethods.

对于每个测试用例我需要的设置和拆卸,我使用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
Run Code Online (Sandbox Code Playgroud)

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

您可以在相应位置添加其他设置和拆卸步骤.

希望能帮助到你!

  • @imsrgadich将测试用例组织成类时, &lt;setup/teardown&gt;_class 用于类的设置和拆卸步骤, &lt;setup/teardown&gt;_method 是每个测试用例方法的相应步骤。 (3认同)
  • 该死……现在我明白了!被困在上面几个小时。所以,要正确看待事情。整个类的“&lt;setup/teardown&gt;_class”。在这里,可以是设置数据库链接或加载数据文件之类的事情。然后,每个测试用例都可以有自己的设置,形式为“&lt;setup/teardown&gt;_method”。现在事情已经很清楚了。多谢! (3认同)

Okk*_*ken 12

如果添加@classmethod装饰器,您的代码应该像您期望的那样工作.

@classmethod 
def setup_class(cls):
    "Runs once per class"

@classmethod 
def teardown_class(cls):
    "Runs at end of class"
Run Code Online (Sandbox Code Playgroud)

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

  • 这几乎与文档中显示的内容完全相同。我在文档中遇到的麻烦是我很难理解上下文:self 传统上被称为 self,而不是 cls,所以这对我来说似乎很奇怪,脱离了类本身的上下文。Kiran(上图)提供了这种背景。 (5认同)
  • @Cognitiaclaeves _“self 传统上称为 self,而不是 cls”_ 是的,`self` 用于实例方法,其中第一个参数是正在执行方法操作的特定对象实例,而 `cls` 是用于`@classmethod`,它绑定到类而不是类的实例(即对象)。 (4认同)

Fed*_*Baù 10

如果你像我一样,想要一次性获得所有可能变化的快速食谱以供以后使用或只是复习一下,这里就有了。

所以,有 2主要的方式来设置/拆卸测试(不包括unittest集成,即当你实际使用unittest时)

1)Fixture(pytest方式)

2)xunit-style(类似于unittest)

这些食谱可以在我的Programming-CookBook中找到-> PyTest Recipes

夹具

夹具设置/拆卸的集合


import pytest

counter = 0

def add_counter():
    global counter
    counter += 1

# ---------------------------
# yield fixtures (recommended)
# ---------------------------


@pytest.fixture
def get_counter():
    print(f"{counter}) -- Fixture (yield)")
    add_counter()
    yield counter


def test_count_is_1(get_counter):
    assert get_counter == 1

def test_count_is_2(get_counter):
    assert get_counter == 2

# ---------------------------
# Adding finalizers directly
# ---------------------------

@pytest.fixture
def get_counter_direct(request):
    print(f"{counter}) -- Fixture (request)")
    add_counter()

    request.addfinalizer(add_counter)
    return counter

def test_count_is_3(get_counter_direct):
    assert get_counter_direct == 3
Run Code Online (Sandbox Code Playgroud)

这导致

================================================================================================================ PASSES ================================================================================================================
___________________________________________________________________________________________________________ test_count_is_1 ____________________________________________________________________________________________________________
-------------------------------------------------------------------------------------------------------- Captured stdout setup ---------------------------------------------------------------------------------------------------------
0) -- Fixture (yield)
___________________________________________________________________________________________________________ test_count_is_2 ____________________________________________________________________________________________________________
-------------------------------------------------------------------------------------------------------- Captured stdout setup ---------------------------------------------------------------------------------------------------------
1) -- Fixture (yield)
___________________________________________________________________________________________________________ test_count_is_3 ____________________________________________________________________________________________________________
-------------------------------------------------------------------------------------------------------- Captured stdout setup ---------------------------------------------------------------------------------------------------------
2) -- Fixture (request)
========================================================================================================== 3 passed in 0.01s ==========================================================================================================
Run Code Online (Sandbox Code Playgroud)

xunit风格

这些都是xunit风格的setup/teardown方式的集合。这里我添加了一个计数器,以便订单可见

import pytest

counter = 0

def add_counter():
    global counter
    counter += 1

# ---------------------------
# Module Level Setup/TearDown
# ---------------------------
def setup_module(module):
    """setup any state specific to the execution of the given module."""
    add_counter()
    print(f"{counter}) -- SetUp (module)")

def teardown_module(module):
    """teardown any state that was previously setup with a setup_module method."""
    add_counter()
    print(f"{counter}) -- tearDown (module)")

class TestSomeClass:

    # ---------------------------
    # Class level setup/teardown
    # ---------------------------
    @classmethod
    def setup_class(cls):
        """setup any state specific to the execution of the given class (which usually contains tests)."""
        add_counter()
        print(f"{counter}) -- SetUp (class)")

    @classmethod
    def teardown_class(cls):
        """teardown any state that was previously setup with a call to setup_class. """
        add_counter()
        print(f"{counter}) -- tearDown (class)")

    # ---------------------------
    # Class level setup/teardown
    # ---------------------------
    def setup_method(self, method):
        """setup any state tied to the execution of the given method in a class.  setup_method is invoked for every test method of a class."""
        add_counter()
        print(f"{counter}) -- SetUp (method)")

    def teardown_method(self, method):
        """teardown any state that was previously setup with a setup_method call. """
        add_counter()
        print(f"{counter}) -- tearDown (method)")

    def test_is_1_a_number(self):
        assert (1).__class__ is int


# ---------------------------
# module level  setup/teardown
# ---------------------------
def setup_function(function):
    """setup any state tied to the execution of the given function. Invoked for every test function in the module."""
    add_counter()
    print(f"{counter}) -- SetUp (function)")


def teardown_function(function):
    """teardown any state that was previously setup with a setup_function call."""
    add_counter()
    print(f"{counter}) -- tearDown (function)")

def test_something_at_module_level():
    assert (1).__class__ is int
Run Code Online (Sandbox Code Playgroud)

这导致

================================================================================================================ PASSES ================================================================================================================
___________________________________________________________________________________________________ TestSomeClass.test_is_1_a_number ___________________________________________________________________________________________________
-------------------------------------------------------------------------------------------------------- Captured stdout setup ---------------------------------------------------------------------------------------------------------
1) -- SetUp (module)
2) -- SetUp (class)
3) -- SetUp (method)
------------------------------------------------------------------------------------------------------- Captured stdout teardown -------------------------------------------------------------------------------------------------------
4) -- tearDown (method)
5) -- tearDown (class)
____________________________________________________________________________________________________ test_something_at_module_level ____________________________________________________________________________________________________
-------------------------------------------------------------------------------------------------------- Captured stdout setup ---------------------------------------------------------------------------------------------------------
6) -- SetUp (function)
------------------------------------------------------------------------------------------------------- Captured stdout teardown -------------------------------------------------------------------------------------------------------
7) -- tearDown (function)
8) -- tearDown (module)
========================================================================================================== 2 passed in 0.01s ===========================================================================================================
Run Code Online (Sandbox Code Playgroud)

文档


Kir*_* Sk 5

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))
Run Code Online (Sandbox Code Playgroud)

为了运行:

pytest nam_of_the_module.py -v 
Run Code Online (Sandbox Code Playgroud)