Python项目的pytest初始化
2018-01-19 00:08:25 +08 字数:2310 标签: Python Test为什么要写测试 ¶
Python是一种必须要写测试的语言。
和C/C++、Java这种需要编译的语言相比,Python这种动态语言天生就有开发迅速的优势。 开发完成后的代码,跑一跑自然知道问题。 然而,如果项目稍大,就难以保证手动测试的可靠性。 因此,江湖传言,Python不适合做大型项目。 当然,江湖上的大多数人是不写测试的。
与Java之类相比,没有100%测试覆盖的Python代码,甚至都不能保证没有语法错误。 而静态语言在预编译时,就会做完整的语法检查,可靠性的差距可见一斑。
然而,正是因为这种差距,Python项目有更迫切的测试需求。 达成100%测试覆盖率的Python项目,可靠性却又比没有写测试的Java项目要强上许多。
为什么是pytest ¶
打开PyCharm,可以看到默认支持的单元测试框架有五类:
unittest是Python标准库之一。
与Java里著名的JUnit4非常类似,通过继承一个TestCase,然后增加test_*
命名的method来测试。
(JUnit5中已经抛弃了这种做法,改为使用注解@Test
的形式。)
它的问题就是,有些过于繁琐。
doctest是一种非常奇特的测试技术,在文档中写测试。 把代码和测试写在一起,这是Java这类语言中没有办法做到的。 虽然它也是Python标准库之一,但孤却第一时间放弃了。 对简单的数学函数,它是很便捷的;但对于复杂的、有副作用(side effect)的function或method来说,它功能不足。 而且,测试代码往往比功能代码更复杂些,不合适写在pydoc里。
Twisted是一个异步网络框架,而Trial则是其单元测试系统,是对unittest的一个封装。 如果不是使用Twisted框架,那么不会使用到它。
nose是一个曾经非常流行的测试,兼容unittest的同时却拥有更加简洁的语法,然而已经停止维护了。 nose本身支持Python 3,然而其很多插件却不支持。 新的项目,不推荐使用它,正如它的文档所说。 非常可惜,孤差点就选它了。
Nose has been in maintenance mode for the past several years and will likely cease without a new person/team to take over maintainership. New projects should consider using Nose2, py.test, or just plain unittest/unittest2.
最终,孤选择了pytest,曾用名py.test。 它能兼容unittest与nose的测试代码,写法简洁,并且还有自己的独到之处。
在《PythonTestingToolsTaxonomy - Python Wiki》还可看到更多不同类型的测试框架。 本文仅介绍pytest测试最基本的写法,以及如何在一个Python项目进行初始化。
pytest测试样例 ¶
与unittest类似的是,pytest也需要把测试function或method命名为test_*
。
方便之处在于,不仅支持function,测试method所在的class也不需要继承什么。
此外,也不需要assert*
系列的function来做检验,直接用内置的assert
即可。
def add(a, b):
return a + b
def test_add():
assert 3 == add(1, 2)
假如以上代码文件名为add.py
,写好后直接运行pytest add.py
,即可开始测试。
测试代码结构 ¶
孤认为,测试代码不应该随着Python包而发布,尽管很多知名的包做出了相反的选择。 测试应该在打包发布前完成。 而发布后,用户是不需要、也不应该去跑测试的。 很多自带测试的包,甚至会干扰当前项目的测试结果。
而功能代码与测试代码的分离,通常采用以下形式:
project
├── setup.cfg
├── setup.py
├── src
│ └── mypkg
│ ├── __init__.py
│ └── something.py
└── tests
├── conftest.py
├── test_init.py
└── test_something.py
源码放在src
下,而测试则放在tests
下。
不把mypkg
直接放在根目录下,是为了避免直接从当前路径导入。
这样虽然方便,但有时会导致安装后的代码的未经测试的(通过测试覆盖率的一些异常可以观察到这一情况)。
详情可以查看这篇著名的博文:《Packaging a python library | ionel’s codelog》,它也是pytest官方文档所推荐的。
tests
可以增加__init__.py
,成为一个Python包,也可以增加子目录、子包。
conftest.py
是pytest的默认配置文件,可以在其中放公用的fixture或plugin。
setup.py与setup.cfg ¶
在setup.py
的tests_require
中,需要配置pytest。
另外,也建议在setup_requires
里配置pytest-runner。
from setuptools import setup
setup(
...
setup_requires=[
'pytest-runner',
],
tests_require=[
'pytest',
],
)
即使不配置pytest,也可直接通过运行pytest
命令来测试。
而做出以上配置后,可以通过python setup.py pytest
命令来测试,并且不用提前安装pytest
。
如果需要通过python setup.py test
的形式执行测试,则需要添加[aliases]
到setup.cfg
。
[aliases]
test=pytest
[tool:pytest]
addopts = --verbose
python_files = tests/*
在setup.cfg
中的[tool:pytest]
块,就是对pytest的配置。
也可在pytest.ini
或tox.ini
中,添加[pytest]
块进行相同配置,效果一样。
不过,配在setup.cfg
,可以在项目根目录少一个文件,孤更喜欢一些。
addopts
是pytest
的命令行参数,--verbose
会让打印输出更细致。
python_files
指定了测试代码的位置。
有了这两个基本的配置,执行测试时就可以不用输入任何参数了。
后续如果有什么新的参数,也都可以配置到这里。
结语 ¶
一个项目的pytest初始化就是这样。 然后,就可以开始愉快地写测试了。