本文将会把关于 Pytest 的内容分上下两篇,上篇主要涉及关于 pytest 概念以及功能组件知识的介绍,下篇主要以一个 Web 项目来将 Pytest 运用实践中。
为什么要做单元测试
相信很多 Python 使用者都会有这么一个经历,为了测试某个模块或者某个函数是否输出自己预期的结果,往往会对产出结果的部分使用 print()
函数将其打印输出到控制台上。
def myfunc(*args, **kwargs):
do_something()
data = ...
print(data)
在一次次改进过程中会不得不经常性地使用 print()
函数来确保结果的准确性,但同时,也由于要测试的模块或者函数变多,代码中也会逐渐遗留着各种未被去掉或注释的 print()
调用,让整个代码变得不是那么简洁得体。
在编程中往往会存在「单元测试」这么一个概念,即指对软件中的最小可测试单元进行检查和验证。这个最小可测单元可以是我们的表达式、函数、类、模块、包中的任意一种或组合,因此我们可以将使用 print()
进行测试的步骤统一地放到单元测试中来进行。
在 Python 中官方早已经为我们内置好了用以进行单元测试的模块 unittest
。但对于新手来说,unittest
在学习曲线上是稍微有点难度的,因为是需要通过继承测试用例类(TestCase
)来进行封装,所以需要对面向对象的知识有足够多的了解;而和类绑定在一起就意味着如果想要实现定制化或者模块解耦,可能就需要多花一些时间在设计划分上。
所以,为了能让测试变得简单且具备可扩展性,一个名为 pytest 的测试框架在 Python 社区中诞生了,使用 pytest 我们可以不用考虑如何基于 TestCase
来实现我们的测试,我们只需要简单到保持我们原有的代码逻辑不变,外加一个 assert
关键字来断言结果,剩下的部分 pytest 会帮我们处理。
# main.py
import pytest
raw_data = read_data(...)
def test_myfunc(*args, **kwargs):
do_something()
data = ...
assert data == raw_data
if __name__ == '__main__':
pytest.main()
之后我们只需要运行包含上述代码的 main.py
文件,就能在终端控制台上看到 pytest 为我们测试得到的结果。如果结果通过,则不会有过多的信息显示,如果测试失败,则会抛出错误信息并告知运行时 data
里的内容是什么。
尽管说 pytest 已经足够简单,但它也提供了许多实用的功能(如:依赖注入),这些功能本身是存在着一些概念层面的知识;但这并不意味着劝退想要使用 pytest 来测试自己代码的人,而是让我们拥有更多的选择,因此只有对 pytest 的这些功能及其概念有了更好地了解,我们才能够充分发挥 pytest 的威力。
快速实现你的第一个 Pytest 测试
通过 pip install pytest
安装 pytest 之后,我们就可以快速实现我们的第一个测试。
首先我们可以任意新建一个 Python 文件,这里我直接以 test_main.py
命名,然后当中留存如下内容:
from typing import Union
import pytest
def add(
x: Union[int, float],
y: Union[int, float],
) -> Union[int, float]:
return x + y
@pytest.mark.parametrize(
argnames="x,y,result",
argvalues=[
(1,1,2),
(2,4,6),
(3.3,3,6.3),
]
)
def test_add(
x: Union[int, float],
y: Union[int, float],
result: Union[int, float],
):
assert add(x, y) == result
之后将终端切换到该文件所处路径下,然后运行 pytest -v
,就会看到 pytest 已经帮我们将待测试的参数传入到测试函数中,并实现对应的结果: