我正在参加「掘金·启航计划」
大家都知道,目前最流行的Python单元测试框架有三种,分别是unittest, nose和pytest。其中unittest是Python自带的测试框架,但问题是比较老了,赶不上时代发展了(哈哈哈);nose2定位是带插件的unittest,实则对unittest的扩展。长远来看,pytest属于潜力股。
通过官网介绍的特点和使用经验,可以将pytest优点总结如下:
1)支持用简单的assert语句实现丰富的断言,无需复杂的self.assert*函数
2)可以自动识别测试模块和测试函数
3)兼容unittest和nose测试集
4)支持参数化
5)支持测试用例的skip和xfail处理
6)可以很好的和jenkins集成
7)支持丰富的插件,例如报告插件pytest-html、allure-pytest、失败重试插件pytest-rerunfailures、
8)活跃的社区,遇到的问题可以高效解决。
简化样板代码
大多数功能测试遵循 Arrange-Act-Assert 模型:
- 设置测试前置条件
- 调用函数来执行测试
- 断言执行结果
测试框架通常会挂接到测试的断言中,以便它们可以在断言失败时提供信息。unittest,例如,提供了许多开箱即用的断言方法,但是不友好的地方是,unittest编写的用例即使是一小部分测试也需要相当数量的样板代码。
下面写一个unittest测试用例并断言在项目中正常工作。
# test_with_unittest.py
from unittest import TestCase
class TryTesting(TestCase):
def test_always_passes(self):
self.assertTrue(True)
def test_always_fails(self):
self.assertTrue(False)
然后,你可以使用命令行运行这些测试:
(venv) $ python -m unittest discover
F.
======================================================================
FAIL: test_always_fails (test_with_unittest.TryTesting)
----------------------------------------------------------------------
Traceback (most recent call last):
File "...\effective-python-testing-with-pytest\test_with_unittest.py",
line 10, in test_always_fails
self.assertTrue(False)
AssertionError: False is not true
----------------------------------------------------------------------
Ran 2 tests in 0.006s
FAILED (failures=1)
一个测试通过,一个测试失败。OK,下面我们总结一下一个完整的测试需要写多少代码:
- 导入类unittest引入TestCase
- 创建TryTesting,一个子类TestCase
- TryTesting为每个测试写一个方法
- 使用self.assert*方法进行断言
这是要编写的大量代码的,所以在开发测试用例时会一遍又一遍地编写相同的样板代码。而pytest则不同,它允许你直接使用普通函数和python assert关键字来简化样板代码达到相同的目的:
# test_with_pytest.py
def test_always_passes():
assert True
def test_always_fails():
assert False
就是如此简单。你需要做的就是写一个带有test_前缀的函数,使用assert关键字断言期望是True/False,然后执行测试即可。
Pytest不仅消除了很多样板代码,而且还提供了更加详细和易于阅读的输出。
更好的测试输出
在项目根目录文件夹下使用pytest命令运行所有测试用例:
(venv) $ pytest
============================= test session starts =============================
platform win32 -- Python 3.10.5, pytest-7.1.2, pluggy-1.0.0
rootdir: ...\effective-python-testing-with-pytest
collected 4 items
test_with_pytest.py .F [ 50%]
test_with_unittest.py F. [100%]
================================== FAILURES ===================================
______________________________ test_always_fails ______________________________
def test_always_fails():
> assert False
E assert False
test_with_pytest.py:7: AssertionError
________________________ TryTesting.test_always_fails _________________________
self = <test_with_unittest.TryTesting testMethod=test_always_fails>
def test_always_fails(self):
> self.assertTrue(False)
E AssertionError: False is not true
test_with_unittest.py:10: AssertionError
======================&