Python高级技术之:`Python`的`doctest`:在文档字符串中编写可执行的测试用例。

各位朋友,晚上好!我是老码,今天咱们来聊聊Python里一个挺有意思的小工具,叫做 doctest。 别看它名字有点儿学术,其实用起来特别接地气,能让你直接在文档字符串里写测试用例,就像给代码写小作文一样。

一、doctest 是个啥?

简单来说,doctest 是 Python 自带的一个模块,它允许你在文档字符串(docstring)里面嵌入测试用例。这些测试用例长得有点像 Python 交互式解释器的会话记录。 doctest 会读取这些会话记录,然后执行里面的代码,检查实际输出是否和文档字符串里写的一样。 如果不一样,就说明你的代码有问题了,赶紧去修bug吧!

二、为什么要用 doctest

你可能会问,现在测试框架那么多,unittest、pytest 哪个不比 doctest 强大? 为什么还要用它呢?

  • 简单易用: doctest 不需要额外的安装,Python 自带。而且语法简单,只要会写 Python 代码,就能写 doctest
  • 文档即测试: doctest 把测试用例和文档放在一起,保证了文档的准确性。 写完代码,顺手写几个测试用例,就相当于给代码写了一份说明书,还带自动验证功能,一举两得。
  • 学习成本低: 对于新手来说,doctest 是一个很好的入门测试的工具。 不需要学习复杂的测试框架,就能体验到测试的乐趣。
  • 示例代码验证: 如果你的文档里有很多示例代码,用 doctest 可以确保这些示例代码是真正可以运行的,而不是摆设。

三、doctest 怎么用?

咱们来点实际的,看看 doctest 到底怎么用。

1. 最简单的例子:

def add(a, b):
    """
    这是一个加法函数。

    >>> add(1, 2)
    3
    >>> add(-1, 1)
    0
    >>> add(0, 0)
    0
    """
    return a + b

if __name__ == "__main__":
    import doctest
    doctest.testmod()

这段代码定义了一个 add 函数,并在它的文档字符串里写了三个测试用例。 每个测试用例都以 >>> 开头,后面跟着 Python 代码,下一行是期望的输出结果。

运行这段代码,doctest 会自动执行文档字符串里的测试用例,如果一切正常,就没有任何输出。 如果有错误,doctest 会告诉你哪个测试用例失败了,以及期望的输出和实际的输出是什么。

2. 稍微复杂一点的例子:

def greet(name):
    """
    打招呼函数。

    >>> greet("Alice")
    'Hello, Alice!'
    >>> greet("Bob")
    'Hello, Bob!'
    >>> greet("")
    'Hello, !'
    """
    return "Hello, " + name + "!"

if __name__ == "__main__":
    import doctest
    doctest.testmod()

这个例子和上一个例子类似,只是函数返回的是字符串。

3. 处理异常:

有时候,我们需要测试函数是否会抛出异常。 doctest 也能处理这种情况。

def divide(a, b):
    """
    除法函数。

    >>> divide(10, 2)
    5.0
    >>> divide(5, 0)
    Traceback (most recent call last):
    ...
    ZeroDivisionError: division by zero
    """
    return a / b

if __name__ == "__main__":
    import doctest
    doctest.testmod()

注意,测试异常的时候,要写出完整的 traceback 信息,包括异常类型和异常信息。 ... 表示可以忽略中间的 traceback 信息。

4. 在模块级别使用 doctest

doctest 不仅可以在函数或类中使用,还可以在模块级别使用。 也就是说,你可以在模块的文档字符串里写测试用例。

"""
这是一个模块级别的 doctest 示例。

>>> 1 + 1
2
>>> "hello".upper()
'HELLO'
"""

def my_function():
    """
    这是一个函数。
    >>> my_function()
    'Function called'
    """
    return "Function called"

if __name__ == "__main__":
    import doctest
    doctest.testmod()

5. 使用 doctest.testfile 测试文件:

除了在文档字符串里写测试用例,还可以把测试用例写在单独的文件里,然后用 doctest.testfile 来运行这些测试用例。

创建一个名为 my_tests.txt 的文件,内容如下:

>>> 1 + 1
2
>>> "world".capitalize()
'World'

然后在 Python 代码里这样写:

import doctest

doctest.testfile("my_tests.txt")

运行这段代码,doctest 就会执行 my_tests.txt 里的测试用例。

四、doctest 的一些高级用法:

doctest 还有一些高级用法,可以让你更灵活地编写测试用例。

1. 使用 doctest 指令:

doctest 提供了一些指令,可以控制测试的行为。 这些指令写在测试用例的第一行,用 #! 开头。

  • # doctest: +SKIP:跳过这个测试用例。
  • # doctest: +ELLIPSIS:忽略输出结果中的省略号 ...
  • # doctest: +NORMALIZE_WHITESPACE:忽略输出结果中的空白差异。
  • # doctest: +IGNORE_EXCEPTION_DETAIL:忽略异常的详细信息。

例如:

def my_list():
    """
    返回一个列表。

    >>> my_list()  # doctest: +ELLIPSIS
    [1, 2, ..., 10]
    """
    return list(range(1, 11))

这个例子使用了 ELLIPSIS 指令,忽略了输出结果中列表的中间部分。 因为我们不想把整个列表都写出来,太长了。

2. 使用 doctest.DocTestSuite

doctest.DocTestSuite 可以把模块、类或函数的文档字符串转换成一个 unittest 测试套件。 这样就可以把 doctest 测试用例和 unittest 测试用例放在一起运行。

import unittest
import doctest

def my_function():
    """
    一个简单的函数。

    >>> my_function()
    'Hello, world!'
    """
    return "Hello, world!"

def load_tests(loader, tests, ignore):
    tests.addTests(doctest.DocTestSuite(my_function))
    return tests

class MyTestCase(unittest.TestCase):
    def test_something(self):
        self.assertEqual(1, 1)

这个例子展示了如何把 my_functiondoctest 测试用例添加到 unittest 测试套件中。

五、doctest 的优缺点:

优点:

  • 简单易用,学习成本低。
  • 文档即测试,保证文档的准确性。
  • 不需要额外的安装,Python 自带。
  • 适合编写简单的测试用例和示例代码验证。

缺点:

  • 功能相对简单,不如专业的测试框架强大。
  • 不适合编写复杂的测试用例。
  • 测试用例和代码耦合在一起,不利于代码的维护。
  • 错误提示信息不够友好。

六、doctest 的适用场景:

  • 编写简单的测试用例。
  • 验证文档中的示例代码。
  • 作为学习测试的入门工具。
  • 小型项目或脚本。

七、doctest 和其他测试框架的比较:

特性 doctest unittest pytest
易用性 非常简单 相对复杂 相对简单
功能 简单,主要用于文档测试 强大,支持各种测试场景 非常强大,支持各种测试场景和插件
文档集成 天然集成,测试用例在文档中 需要单独编写测试用例 需要单独编写测试用例
学习曲线 非常低 较高 较低
适用场景 简单测试,文档示例,小型项目 中大型项目,需要复杂测试逻辑 中大型项目,需要灵活的测试框架和插件支持

八、一些建议:

  • doctest 适合编写简单的测试用例,对于复杂的测试,还是建议使用专业的测试框架,比如 unittestpytest
  • doctest 的测试用例应该尽量简洁明了,避免在文档字符串里写太多的代码。
  • doctest 的错误提示信息不够友好,需要仔细阅读 traceback 信息才能找到错误原因。
  • doctest 可以作为学习测试的入门工具,让你快速了解测试的基本概念。
  • 在编写文档的时候,可以考虑使用 doctest 来验证文档中的示例代码,保证文档的准确性。

九、总结:

doctest 是 Python 里一个简单易用的测试工具,它可以让你直接在文档字符串里编写测试用例。 虽然它的功能相对简单,但对于编写简单的测试用例、验证文档中的示例代码、以及作为学习测试的入门工具来说,还是非常实用的。

好了,今天的分享就到这里,希望大家能喜欢上 doctest 这个小工具,让你的代码更加健壮,文档更加准确! 咱们下次再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注