如何将 doctest 与日志一起使用?

Ali*_*Ali 8 python logging doctest

以下 doctest 失败:

import logging
logging.basicConfig(level=logging.DEBUG,format='%(message)s')

def say_hello():
  '''
  >>> say_hello()
  Hello!
  '''
  logging.info('Hello!')

if __name__ == '__main__':
    import doctest
    doctest.testmod()
Run Code Online (Sandbox Code Playgroud)

这些页面

似乎暗示logging.StreamHandler(sys.stdout),logger.addHandler(handler)但我的尝试在这方面失败了。(如果不是很明显,我是 python 的新手。)

请帮我修复上面的代码,以便测试通过。


2017 年 6 月 4 日更新:回答00prometheus的评论:当我问这个问题时,接受的使用 doctest 和登录 python 程序的答案似乎不必要地复杂。确实如此,因为这里接受的答案给出了一个更简单的解决方案。在我非常有偏见的意见中,我的问题也比我在原始帖子中已经链接的问题更清楚。

djh*_*ese 3

您需要定义一个“记录器”对象。这通常在导入后完成:

import sys
import logging
log = logging.getLogger(__name__)
Run Code Online (Sandbox Code Playgroud)

当您想要记录消息时:

log.info('Hello!')
Run Code Online (Sandbox Code Playgroud)

在像脚本一样运行的代码中,您设置 basicConfig:

if __name__ == '__main__':
    import doctest
    logging.basicConfig(level=logging.DEBUG, stream=sys.stdout, format='%(message)s')
    doctest.testmod()
Run Code Online (Sandbox Code Playgroud)

编辑:

好吧,你是对的。它不起作用,但我让它起作用......但不要这样做!只需使用打印语句或返回您实际需要检查的内容。正如您的第二个链接所说,这只是一个坏主意。您不应该检查日志输出(用于日志记录)。甚至第二个链接的原始发布者也表示,他们通过将日志记录切换为使用打印来使其正常工作。但这是似乎有效的邪恶代码:

class MyDocTestRunner(doctest.DocTestRunner):
    def run(self, test, compileflags=None, out=None, clear_globs=True):
        if out is None:
            handler = None
        else:
            handler = logging.StreamHandler(self._fakeout)
            out = sys.stdout.write
        logger = logging.getLogger() # root logger (say)
        if handler:
            logger.addHandler(handler)
        try:
            doctest.DocTestRunner.run(self, test, compileflags, out, clear_globs)
        finally:
            if handler:
                logger.removeHandler(handler)
                handler.close()
    
if __name__ == '__main__':
    logging.basicConfig(level=logging.DEBUG, format='%(message)s')
    tests = doctest.DocTestFinder().find(say_hello, __name__)
    dt_runner = MyDocTestRunner()
    for t in tests:
        dt_runner.run(t, out=True)
Run Code Online (Sandbox Code Playgroud)

编辑(续):

在尝试您的第二个链接建议的内容时,我的尝试也失败了。这是因为 doctest 在内部将 sys.stdout 重新分配给self._fakeout. 这就是为什么除了我的黑客之外什么都行不通。我实际上告诉记录器写入这个“fakeout”。

编辑(评论的答案):

这不完全是链接中的代码。如果它是链接中的代码,我会说这并不是一个糟糕的选择,因为它没有做任何太复杂的事情。然而,我的代码使用的是普通用户不应使用的“私有”内部实例属性。这就是为什么它是邪恶的。

是的,日志记录可用于测试输出,但在单元测试/文档测试中这样做没有多大意义,这可能就是为什么文档测试不包含这样的现成功能的原因。您链接到的 TextTest 内容都是关于功能或集成测试的。单元测试(和文档测试)应该测试小的单独组件。如果您必须捕获记录的输出以确保您的单元测试/文档测试正确,那么您可能应该考虑将内容分开或不在文档测试中进行这些检查。

我个人只使用文档测试来进行简单的示例和验证。主要用于使用示例,因为任何用户都可以看到内联文档测试。

编辑(好的最后一个):

相同的解决方案,更简单的代码。此代码不需要您创建自定义运行程序。您仍然需要创建默认的运行程序和其他内容,因为您需要访问“_fakeout”属性。如果不将此属性作为流记录,则无法使用 doctest 检查日志记录输出。

if __name__ == '__main__':
    dt_runner = doctest.DocTestRunner()
    tests = doctest.DocTestFinder().find(sys.modules[__name__])
    logging.basicConfig(level=logging.DEBUG, format='%(message)s', stream=dt_runner._fakeout)
    for t in tests:
        dt_runner.run(t)
Run Code Online (Sandbox Code Playgroud)