Python 高手编程系列三千三百八十一:达式 Python 标准测试工具
Python 在标准库中提供了两个主要模块来编写测试。● unittesthttps://docs.python.org/3/library/unittest.html这是标准库也是最常见的 Python 单元测试框架它基于 Java 的 JUnit 框架最初由 Steve Purcell 编写以前称为 PyUnit。● doctesthttps://docs.python.org/3/library/doctest.html这是一个有读写能力的编程测试工具它带有交互式使用示例。unittestunittest 基本上提供了 Java 中的 Junit 框架的功能。它提供了一个名为 TestCase的基类它有一组广泛的方法来验证函数调用和语句的输出。该模块是为编写单元测试而创建的但是只要测试使用用户接口验收测试也可以用它来编写。例如一些测试框架提供帮助在 unittest 之上驱动工具如 Selenium。使用 unittest 为一个模块编写一个简单的单元测试这是通过继承 TestCase 类并且使用 test 前缀来编写方法来完成的。测试驱动开发原则部分的最后一个例子如下所示import unittestfrom primes import is_primeclass MyTests(unittest.TestCase):def test_is_prime(self):self.assertTrue(is_prime(5))self.assertTrue(is_prime(7))self.assertFalse(is_prime(8))self.assertFalse(is_prime(0))self.assertFalse(is_prime(1))self.assertFalse(is_prime(-1))self.assertFalse(is_prime(-3))self.assertFalse(is_prime(-6))ifname “main”:unittest.main()unittest.main()函数是一个通用程序它允许将整个模块作为一个测试套件执行如下所示$ python test_is_prime.py -vtest_is_prime (main.MyTests) … okRan 1 test in 0.000sOKunittest.main()函数扫描当前模块的上下文并查找 TestCase 类的子类。它实例化这些子类然后运行所有以 test 作为前缀开头的方法。一个好的测试套件遵循通用和一致的命名约定。例如如果 primes.py 模块中包含is_prime 函数则测试类可以命名为 PrimesTests 并放入 test_primes.py 文件中如下所示import unittestfrom primes import is_primeclass PrimesTests(unittest.TestCase):def test_is_prime(self):self.assertTrue(is_prime(5))self.assertTrue(is_prime(7))self.assertFalse(is_prime(8))self.assertFalse(is_prime(0))self.assertFalse(is_prime(1))self.assertFalse(is_prime(-1))self.assertFalse(is_prime(-3))self.assertFalse(is_prime(-6))ifname ‘main’:unittest.main()从那里每次在 utils 模块中进行开发就在 test_primes 模块中编写更多的测试。为了运行测试test_primes 模块需要在上下文中获得 primes 模块。这可以通过将两个模块放在同一个包中通过将测试模块显式地添加到 Python 路径来实现。在实践中setuptools 的 develop 命令在这里非常有用。对整个应用程序运行测试的前提是你拥有一个脚本它可以在所有测试模块中构建测试活动test campaign。unittest 提供了一个 TestSuite 类可以聚合测试并将它们作为测试活动运行只要它们都是 TestCase 或 TestSuite 的实例。在以往的 Python 中有这样一个约定测试模块提供一个 test_suite 函数该函数返回一个 TestSuite 实例当模块被命令提示符调用或被测试运行器使用时在__main__部分会调用它如下所示import unittestfrom primes import is_primeclass PrimesTests(unittest.TestCase):def test_is_prime(self):self.assertTrue(is_prime(5))self.assertTrue(is_prime(7))self.assertFalse(is_prime(8))self.assertFalse(is_prime(0))self.assertFalse(is_prime(1))self.assertFalse(is_prime(-1))self.assertFalse(is_prime(-3))self.assertFalse(is_prime(-6))class OtherTests(unittest.TestCase):def test_true(self):self.assertTrue(True)def test_suite():“”“构建测试套件”“”suite unittest.TestSuite()suite.addTests(unittest.makeSuite(PrimesTests))suite.addTests(unittest.makeSuite(OtherTests))return suiteifname ‘main’:unittest.main(defaultTest‘test_suite’)在 shell 中运行此模块将打印测试活动输出如下所示$ python test_primes.py -vtest_is_prime (main.PrimesTests) … oktest_true (main.OtherTests) … okRan 2 tests in 0.001sOK在旧版本的 Python 中unittest 模块没有正确的测试发现实用程序就需要使用前面的方法。通常所有测试的运行都是通过一个全局脚本来完成的该脚本浏览代码树寻找测试并运行它们。这个过程被称为测试发现test discovery将在本章后面继续讨论。现在你应该只知道 unittest 提供了一个简单的命令可以从模块和包中发现带有 test前缀的所有测试如下所示$ python -m unittest -vtest_is_prime (test_primes.PrimesTests) … oktest_true (test_primes.OtherTests) … okRan 2 tests in 0.001sOK如果使用上述命令则不需要手动定义__main__部分并调用 unittest.main()函数。doctestdoctest 是一个模块它通过从 docstrings 或文本文件中提取片段以交互式提示会话的形式重放它们以检查示例输出是否与实际输出相同。例如具有以下内容的文本文件可以作为测试运行Check addition of integers works as expected::1 12让我们假设这个文档文件存储在以 test.rst 命名的文件中。doctest 模块提供了一些功能可以从以下这些文档中提取和运行测试import doctestdoctest.testfile(‘test.rst’, verboseTrue)Trying:1 1Expecting:2ok1 items passed all tests:1 tests in test.rst1 tests in 1 items.1 passed and 0 failed.Test passed.TestResults(failed0, attempted1)使用 doctest 有很多优点。● 包可以通过实例文档化和测试。● 文档示例始终是最新的。● 在 doctests 中使用示例编写包有助于维护用户的观点。然而文档测试并不能替代单元测试它们仅应用于在文档中提供可读的示例。换句话说当测试涉及底层的事情或着需要复杂的会使文档混淆的测试固件test fixtures时就不应该使用它们了。一些 Python 框架如 Zope广泛使用文档测试有时它们会被新手批判。有些doctests 真的很难阅读和理解因为示例打破了技术写作的规则之一它们不能被放到命令行里直接运行它们需要广泛的知识。因此本应该有助于新手的文档真的很难阅读因为代码示例是通过 TDD 构建的文档测试是基于复杂的测试固件或者特定的测试 API。