测试类和方法
使用 Pytest 除了能编写测试函数外,还能使用类。 正如我们已经提到的,不需要继承,测试类只遵循一些简单的规则。 使用类可以提高灵活性和可重用性。 正如你接下来会看到的,Pytest 不会妨碍你,并会避免强制你以某种方式编写测试。
与函数一样,仍然可以使用 assert
语句编写断言。
生成测试类
让我们使用一个实际场景来了解测试类如何提供帮助。 以下函数检查给定文件在其内容中是否包含“yes”。 如果包含,则返回 True
。 如果文件不存在,或在其内容中包含“no”,则返回 False
。 这种情况在使用文件系统指示完成情况的异步任务中很常见。
该函数如下所示:
import os
def is_done(path):
if not os.path.exists(path):
return False
with open(path) as _f:
contents = _f.read()
if "yes" in contents.lower():
return True
elif "no" in contents.lower():
return False
下面是名为 test_files.py 的文件中的具有两个测试(每个条件一个测试)的类:
class TestIsDone:
def test_yes(self):
with open("/tmp/test_file", "w") as _f:
_f.write("yes")
assert is_done("/tmp/test_file") is True
def test_no(self):
with open("/tmp/test_file", "w") as _f:
_f.write("no")
assert is_done("/tmp/test_file") is False
注意
测试方法对临时测试文件使用 /tmp 路径,因为它更容易用于示例。 但是,如果需要使用临时文件,请考虑使用 tempfile
之类的库,它可安全地创建(和删除)这些文件。 并非所有系统都有 /tmp 目录,并且该位置可能不是临时的,具体取决于操作系统。
若使用 -v
标志运行测试以增加详细程度,可显示测试通过:
pytest -v test_files.py
============================= test session starts ==============================
Python 3.9.6, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
cachedir: .pytest_cache
rootdir: /private/
collected 2 items
test_files.py::TestIsDone::test_yes PASSED [ 50%]
test_files.py::TestIsDone::test_no PASSED [100%]
============================== 2 passed in 0.00s ===============================
虽然通过了测试,但它们看起来存在重复,并且在测试完成后还会留下文件。 让我们先在下一部分中了解帮助程序方法,然后再了解如何改进测试。
帮助器方法
在测试类中,可以使用几种方法来设置和拆解测试执行。 如果定义了测试,Pytest 会自动执行。 若要使用这些方法,应知道它们具有特定的顺序和行为。
setup
:在类中的每个测试之前执行一次teardown
:在类中的每个测试之后执行一次setup_class
:在类中的所有测试之前执行一次teardown_class
:在类中的所有测试之后执行一次
如果测试需要类似的(或相同的)资源才能正常工作时,编写设置方法会很有用。 理想情况下,测试完成后不应留下资源,因此在这些情况下,拆解方法可以帮助进行测试清理。
清理
现在来看一个更新的测试类,它会在每次测试后清理文件:
class TestIsDone:
def teardown(self):
if os.path.exists("/tmp/test_file"):
os.remove("/tmp/test_file")
def test_yes(self):
with open("/tmp/test_file", "w") as _f:
_f.write("yes")
assert is_done("/tmp/test_file") is True
def test_no(self):
with open("/tmp/test_file", "w") as _f:
_f.write("no")
assert is_done("/tmp/test_file") is False
由于使用了 teardown()
方法,所以此测试类不再遗留 /tmp/test_file。
设置
我们可以对此类执行的另一个改进是添加指向文件的变量。 由于该文件现已在六个位置声明,因此对路径的任何更改都意味着在所有这些位置更改它。 此示例展示添加了声明路径变量的 setup()
方法后该类是什么样子:
class TestIsDone:
def setup(self):
self.tmp_file = "/tmp/test_file"
def teardown(self):
if os.path.exists(self.tmp_file):
os.remove(self.tmp_file)
def test_yes(self):
with open(self.tmp_file, "w") as _f:
_f.write("yes")
assert is_done(self.tmp_file) is True
def test_no(self):
with open(self.tmp_file, "w") as _f:
_f.write("no")
assert is_done(self.tmp_file) is False
自定义帮助程序方法
可以在类中创建自定义帮助程序方法。 这些方法不得以名称 test
为前缀,并且不能命名为安装程序或清理方法。 在 TestIsDone
类中,我们可以在自定义帮助程序中自动创建临时文件。 该自定义帮助程序方法可能如以下示例所示:
def write_tmp_file(self, content):
with open(self.tmp_file, "w") as _f:
_f.write(content)
Pytest 不会自动执行 write_tmp_file()
方法,其他方法可以直接调用该方法以保存重复的任务,例如写入文件。
将测试方法更新为使用自定义帮助程序后,整个类如下例所示:
class TestIsDone:
def setup(self):
self.tmp_file = "/tmp/test_file"
def teardown(self):
if os.path.exists(self.tmp_file):
os.remove(self.tmp_file)
def write_tmp_file(self, content):
with open(self.tmp_file, "w") as _f:
_f.write(content)
def test_yes(self):
self.write_tmp_file("yes")
assert is_done(self.tmp_file) is True
def test_no(self):
self.write_tmp_file("no")
assert is_done(self.tmp_file) is False
何时使用类而不是函数
关于何时使用类而不是函数没有任何严格的规则。 一个好的做法是持续遵循项目和团队中一直使用的惯例做法。 可通过下面这些常用问题来帮助确定何时使用类:
- 测试是否需要类似的设置或清理帮助程序代码?
- 将测试组合在一起是否有逻辑上的意义?
- 测试套件中是否至少有一些测试?
- 测试能否受益于常用的帮助程序函数?