如何正确设置和拆除我的pytest测试类?
我正在使用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
吗?
8 个回答
如果你在代码中加上 @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/
正如@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..."
所以,运行 test_1...
会产生:
I setup... and now I'm testing things...
I setup ... but now I'm torn down...
注意到 stuff_i_setup
在这个“fixture”中被引用,这样就可以在测试开始前准备这个对象,并在测试结束后清理它。你可以想象,这对于一些需要保持状态的对象,比如一个假设中的数据库或者某个连接,都是很有用的,因为在每次测试运行之前都需要清理这些对象,以确保测试之间是相互独立的。
这可能会对你有帮助: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 的时候,它会记录开始执行和结束执行的详细信息,方法也是一样。
你可以在相应的位置添加其他的准备和清理步骤。
希望这对你有帮助!
当你提到 "作为类方法定义的测试" 时,你是指真正的 类方法(这些方法的第一个参数是类)还是普通的方法(这些方法的第一个参数是实例)呢?
因为你的例子中测试方法使用了 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
,不过如果你的设置/清理代码不需要知道测试的上下文,这个实例可以忽略不计。更多信息可以在 这里 找到。
我还建议你了解一下 py.test 的 fixtures,因为它们是一个更强大的概念。
根据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 ===
另一种编写清理代码的方法是,在你的fixture函数中接受一个request
上下文对象,然后调用它的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))