如何通过测试正确设置和拆卸我的 pytest 类?
Posted
技术标签:
【中文标题】如何通过测试正确设置和拆卸我的 pytest 类?【英文标题】:How do I correctly setup and teardown for my pytest class with tests? 【发布时间】:2014-12-11 21:04:06 【问题描述】:我正在使用 selenium 进行端到端测试,但我不知道如何使用 setup_class
和 teardown_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_class
和teardown_class
?
【问题讨论】:
【参考方案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") ``
链接更新:docs.pytest.org/en/stable/…【参考方案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_method
和teardown_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_class
和setup_method
有什么区别?
@imsrgadich 当您将测试用例组织成类时,<setup/teardown>_class
。在这里,可以是设置到 DB 的链接或加载数据文件。然后,每个测试用例都可以有自己的设置,格式为<setup/teardown>_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
,允许该对象为setup
和torn 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
用于@classmethod
s,它们绑定到类而不是类的实例(即对象)。【参考方案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
【讨论】:
以上是关于如何通过测试正确设置和拆卸我的 pytest 类?的主要内容,如果未能解决你的问题,请参考以下文章