05:单元测试和 pytest
¶
为项目的python代码提供单元测试。
背景¶
正如咒语所说,“未经测试的代码就是被破坏的代码。”Python社区有一个很长的编写测试脚本的文化,它可以确保您的代码在编写时正确工作,并在将来维护它。Pyramid一直致力于测试,从最早的预发行版开始,测试覆盖率达到100%。
python包含 unit testing framework 在它的标准库中。多年来,许多Python项目,例如 pytest ,已将此框架扩展为提供更多便利和功能的可选测试运行程序。Pyramid开发者使用 pytest
,我们将在本教程中使用。
别担心,本教程不会对“测试驱动开发”(TDD)太学究。我们将做足够的工作来确保在每一步中,我们没有主要地破坏代码。在编写代码时,您可能会发现这比经常更改浏览器并单击“重新加载”更方便。
我们还将讨论 pytest-cov 另一部分。
目标¶
编写单元测试以确保代码的质量。
安装python包 (
pytest
)这有助于我们的测试。
步骤¶
首先,我们复制上一步的结果。
cd ..; cp -r debugtoolbar unit_testing; cd unit_testing
添加
pytest
对我们项目的依赖setup.py
作为一个 Setuptools “额外”:1from setuptools import setup 2 3# List of dependencies installed via `pip install -e .` 4# by virtue of the Setuptools `install_requires` value below. 5requires = [ 6 'pyramid', 7 'waitress', 8] 9 10# List of dependencies installed via `pip install -e ".[dev]"` 11# by virtue of the Setuptools `extras_require` value in the Python 12# dictionary below. 13dev_requires = [ 14 'pyramid_debugtoolbar', 15 'pytest', 16] 17 18setup( 19 name='tutorial', 20 install_requires=requires, 21 extras_require={ 22 'dev': dev_requires, 23 }, 24 entry_points={ 25 'paste.app_factory': [ 26 'main = tutorial:main' 27 ], 28 }, 29)
安装我们的项目及其新添加的依赖项。注意,我们使用了额外的说明符
[dev]
为开发安装测试需求,并用双引号将其和周期包围起来。$VENV/bin/pip install -e ".[dev]"
现在我们编写一个简单的单元测试
unit_testing/tutorial/tests.py
:1import unittest 2 3from pyramid import testing 4 5 6class TutorialViewTests(unittest.TestCase): 7 def setUp(self): 8 self.config = testing.setUp() 9 10 def tearDown(self): 11 testing.tearDown() 12 13 def test_hello_world(self): 14 from tutorial import hello_world 15 16 request = testing.DummyRequest() 17 response = hello_world(request) 18 self.assertEqual(response.status_code, 200)
现在运行测试:
$VENV/bin/pytest tutorial/tests.py -q . 1 passed in 0.14 seconds
分析¶
我们的 tests.py
导入python标准单元测试框架。为了使编写面向Pyramid的测试更加方便,Pyramid提供了一些 pyramid.testing
我们在测试设置和拆卸中使用的帮助程序。我们的一个测试导入视图,发出一个虚拟请求,并查看视图是否返回我们期望的结果。
这个 tests.TutorialViewTests.test_hello_world
测试是单元测试的一个小例子。首先,我们在每个测试中导入视图。为什么不在顶部导入,就像在普通的Python代码中一样?因为导入会导致破坏测试的结果。我们希望我们的测试 单位 因此得名 unit 测试。每个测试都应该将自身隔离到正确的程度。
然后,我们的测试生成一个假的传入Web请求,然后调用我们的Pyramid视图。我们在响应上测试HTTP状态代码,以确保它符合我们的期望。
注意我们使用 pyramid.testing.setUp()
和 pyramid.testing.tearDown()
这里实际上不需要;只有当测试需要使用 config
对象(它是一个配置程序),在调用视图之前将内容添加到配置状态。
额外credit¶
更改测试以断言响应状态代码应为
404
(意思是找不到)。跑pytest
再一次。阅读错误报告,看看你是否能破译它告诉你的。作为一个更现实的例子,把
tests.py
回到找到它时,在视图中放一个错误,例如对一个不存在的变量的引用。运行测试,看看这比重新加载浏览器和返回代码更方便。最后,对于最现实的测试,阅读Pyramid
Response
对象,并查看如何更改响应代码。运行测试,看看测试是如何确认代码声称支持的“契约”的。我们如何添加单元测试断言来测试响应主体的HTML值?
我们为什么要进口
hello_world
视图函数 里面 这个test_hello_world
方法而不是在模块顶部?