在doctest字符串中嵌入测试代码或数据

2 投票
2 回答
626 浏览
提问于 2025-04-16 01:33

我想在一个文件里的多个doctest中共享一些测试数据和/或函数。有没有办法做到这一点,而不需要把它们放在外部文件里或者被测试文件的代码中呢?

更新

"""This is the docstring for the module ``fish``.

I've discovered that I can access the module under test
  from within the doctest, and make changes to it, eg


>>> import fish
>>> fish.data = {1: 'red', 2: 'blue'}
"""

def jef():
    """
    Modifications made to the module will persist across subsequent tests:

    >>> import fish
    >>> fish.data[1]
    'red'
    """
    pass

def seuss():
    """
    Although the doctest documentation claims that
      "each time doctest finds a docstring to test,
       it uses a shallow copy of M‘s globals",
      modifications made to the module by a doctest
      are not imported into the context of subsequent docstrings:

    >>> data
    Traceback (most recent call last):
      ...
    NameError: name 'data' is not defined
    """
    pass

我猜想doctest会先复制一次模块,然后再为每个文档字符串复制一份?

无论如何,把模块导入到每个文档字符串中似乎是可行的,虽然有点麻烦。

我更希望能使用一个单独的命名空间,这样可以避免不小心覆盖掉实际模块的数据,这些数据可能会在后续的测试中以某种未记录的方式被导入。

我想到理论上可以动态创建一个模块来包含这个命名空间。不过到目前为止,我还没有从我之前问的问题中得到任何关于如何做到这一点的指引。任何信息都非常欢迎!(作为对相关问题的回应)

总之,我希望这些更改能直接传播到后续文档字符串的命名空间中。所以我最初的问题依然存在,只是加上了这个条件。

2 个回答

0

可行的,虽然可能没有被大肆宣传。

如果想要获得带有测试的模块,并且这些测试都使用一个共享的执行环境(也就是说,单个测试可以共享和重用它们的结果),你需要查看相关文档,里面提到:

... 每次 doctest 找到一个需要测试的文档字符串时,它会使用 浅拷贝副本 来获取 M 的全局变量,这样运行测试就不会改变模块的真实全局变量,并且一个测试在 M 中不会留下痕迹,导致另一个测试意外地成功。

...

你可以通过将 globs=your_dict 传递给 testmod()testfile()强制使用 你自己的 字典 作为执行环境。

基于这个信息,我成功地逆向工程doctest 模块,发现除了使用副本(也就是 dictcopy() 方法),它还会在每个测试后清空全局字典(使用 clear())。

因此,你可以用类似下面的方式来修改自己的全局字典:

class Context(dict):
    def clear(self):
        pass
    def copy(self):
        return self 

然后可以这样使用:

import doctest
from importlib import import_module

module = import_module('some.module')
doctest.testmod(module,
                # Make a copy of globals so tests in this
                # module don't affect the tests in another
                glob=Context(module.__dict__.copy()))
2

这就是为什么很多人对文档测试(doctests)感到失望的原因:随着测试变得越来越复杂,你需要真正的编程工具来设计你的测试,就像你设计产品代码一样。

我觉得在文档测试中没有办法直接包含共享的数据或函数,除了在你的产品代码中定义它们,然后在文档测试中使用。

你需要用真实的代码来定义一些测试基础设施。如果你喜欢文档测试,你可以在这些文档测试中使用这些基础设施。

撰写回答