4

以下(废话)Python 模块的 doctest 失败:

"""
>>> L = []
>>> if True:
...    append_to(L) # XXX
>>> L
[1]
"""

def append_to(L):
    L.append(1)
    class A(object):
        pass
    return A()

import doctest; doctest.testmod()

这是因为标记为 XXX 的行之后的输出是<__main__.A object at ...>(由 返回append_to)。当然,我可以将这个输出直接放在标记为 XXX 的行之后,但在我的情况下,这会分散读者对实际测试内容的注意力,即函数的副作用append_to。那么如何抑制该输出或如何忽略它。我试过了:

"""
>>> L = []
>>> if True:
...    append_to(L) # doctest: +ELLIPSIS
    ...
>>> L
[1]
"""

def append_to(L):
    L.append(1)
    class A(object):
        pass
    return A()

import doctest; doctest.testmod()

但是,这会产生一个ValueError: line 4 of the docstring for __main__ has inconsistent leading whitespace: ' ...'.

我不想做的是将行更改为append_to(L)_ = append_to(L)抑制输出的内容,因为 doctest 用于文档目的,并向读者展示应该如何使用模块。(在记录的情况下,append_to应该使用类似语句而不是函数。写作_ = append_to(L)会使读者偏离这一点。)

4

3 回答 3

7

重写:这现在确实有效;我意识到我之前写的“doctest”实际上没有被解析为模块文档字符串,所以测试没有通过:它只是没有运行。

我确保仔细检查了这个。

__doc__ = """
>>> L = []
>>> if True:
...    append_to(L) # doctest: +IGNORE_RESULT
>>> L
[1]
""".replace('+IGNORE_RESULT', '+ELLIPSIS\n<...>')

def append_to(L):
    L.append(1)
    class A(object):
        pass
    return A()

我不确定这是否更具可读性。请注意,没有什么特别之处<...>:只有在实际返回值具有这种形式时它才会起作用,就像在这种情况下一样(即它是<module.A object at 0x...>)。ELLIPSIS 选项使...“匹配实际输出中的任何子字符串” ¹。所以我认为没有办法让它匹配整个输出。

更新:要以“正确”的方式执行此操作,您似乎想要调用doctest.register_optionflag('IGNORE_RESULT'), subclassdoctest.OptionChecker并安排该子类的实例以供 doctest 使用。大概这意味着运行您的 doctest via$ python -m doctest your_module.py不是一种选择。

于 2010-10-05T10:35:54.407 回答
0

我最终遇到了这个问题,因为我需要 +IGNORE_RESULT 行为,但需要在 sphinx 文档上运行 doc 测试。@intuited 发布的replace答案在这种情况下不起作用,并且没有提到的“正确”解决方案的代码。因此,我发布了我最终得到的结果。

作为对该问题的直接回答,解决方案将是:

__doc__ = """
>>> L = []
>>> if True:
...    append_to(L) # doctest: +IGNORE_RESULT
>>> L
[1]
"""

def append_to(L):
    L.append(1)
    class A(object):
        pass
    return A()

if __name__ == "__main__":
    import doctest

    IGNORE_RESULT = doctest.register_optionflag('IGNORE_RESULT')

    OutputChecker = doctest.OutputChecker
    class CustomOutputChecker(OutputChecker):
        def check_output(self, want, got, optionflags):
            if IGNORE_RESULT & optionflags:
                return True
            return OutputChecker.check_output(self, want, got, optionflags)

    doctest.OutputChecker = CustomOutputChecker
    doctest.testmod()

对于我对 doc 测试 sphinx 文档的特殊需要,请添加到conf.py文件中:

import doctest

IGNORE_RESULT = doctest.register_optionflag('IGNORE_RESULT')

OutputChecker = doctest.OutputChecker
class CustomOutputChecker(OutputChecker):
    def check_output(self, want, got, optionflags):
        if IGNORE_RESULT & optionflags:
            return True
        return OutputChecker.check_output(self, want, got, optionflags)

doctest.OutputChecker = CustomOutputChecker

然后使用如下命令进行测试:

sphinx-build -M doctest source_dir build_dir source_dir/file.rst
于 2021-10-30T17:15:40.597 回答
-2

请尽量给出完全独立、可运行的代码;即使在演示问题时,代码也应自行运行以重现问题,因此解决方案可以直接复制代码以演示答案。

我不知道对此有一个干净的解决方案,而且我以前也遇到过;这似乎是 doctests 提供的模糊(更直白地说:草率)测试定义的副作用。一种解决方法是记住您可以在 doctests 中定义函数,因此您可以将整个测试包含为单个函数而不是单个语句。

def append_to(l):
    """
    >>> L = []
    >>> def test():
    ...     if True:
    ...         append_to(L) # XXX
    >>> test()
    >>> L
    [1]

    >>> def test():
    ...     L = []
    ...     if True:
    ...         append_to(L) # XXX
    ...     return L
    >>> test()
    [1]

    """
    l.append(1)
    return object()

if __name__ == "__main__":
    import doctest
    doctest.testmod()
于 2010-10-05T09:47:02.740 回答