在doctest字符串中嵌入测试代码或数据
我想在一个文件里的多个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 个回答
这 是 可行的,虽然可能没有被大肆宣传。
如果想要获得带有测试的模块,并且这些测试都使用一个共享的执行环境(也就是说,单个测试可以共享和重用它们的结果),你需要查看相关文档,里面提到:
... 每次
doctest
找到一个需要测试的文档字符串时,它会使用 浅拷贝 的 副本 来获取M
的全局变量,这样运行测试就不会改变模块的真实全局变量,并且一个测试在M
中不会留下痕迹,导致另一个测试意外地成功。...
你可以通过将
globs=your_dict
传递给testmod()
或testfile()
来强制使用 你自己的 字典 作为执行环境。
基于这个信息,我成功地逆向工程 了 doctest
模块,发现除了使用副本(也就是 dict
的 copy()
方法),它还会在每个测试后清空全局字典(使用 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()))
这就是为什么很多人对文档测试(doctests)感到失望的原因:随着测试变得越来越复杂,你需要真正的编程工具来设计你的测试,就像你设计产品代码一样。
我觉得在文档测试中没有办法直接包含共享的数据或函数,除了在你的产品代码中定义它们,然后在文档测试中使用。
你需要用真实的代码来定义一些测试基础设施。如果你喜欢文档测试,你可以在这些文档测试中使用这些基础设施。