使用doctest测试警告

Eli*_*ght 9 python doctest warnings

我想使用doctests测试某些警告的存在.例如,假设我有以下模块:

from warnings import warn

class Foo(object):
    """
    Instantiating Foo always gives a warning:

    >>> foo = Foo()
    testdocs.py:14: UserWarning: Boo!
      warn("Boo!", UserWarning)
    >>> 
    """

    def __init__(self):
        warn("Boo!", UserWarning)
Run Code Online (Sandbox Code Playgroud)

如果我跑到python -m doctest testdocs.py我班上运行doctest并确保打印警告,我得到:

testdocs.py:14: UserWarning: Boo!
  warn("Boo!", UserWarning)
**********************************************************************
File "testdocs.py", line 7, in testdocs.Foo
Failed example:
    foo = Foo()
Expected:
    testdocs.py:14: UserWarning: Boo!
      warn("Boo!", UserWarning)
Got nothing
**********************************************************************
1 items had failures:
   1 of   1 in testdocs.Foo
***Test Failed*** 1 failures.
Run Code Online (Sandbox Code Playgroud)

看起来警告正在打印但未被doctest捕获或注意到.我猜这是因为警告打印sys.stderr而不是sys.stdout.但即使我sys.stderr = sys.stdout在模块结束时说这种情况也会发生.

那么有没有办法使用doctests来测试警告?我在文档或Google搜索中都没有提到这种方式.

Sco*_*son 5

Python 文档的测试警告部分专门讨论此主题。但是,总而言之,您有两种选择:

(A) 使用catch_warnings上下文管理器

这是官方文档中推荐的课程。然而,catch_warnings上下文管理器仅在 Python 2.6 中出现。

import warnings

def fxn():
    warnings.warn("deprecated", DeprecationWarning)

with warnings.catch_warnings(record=True) as w:
    # Cause all warnings to always be triggered.
    warnings.simplefilter("always")
    # Trigger a warning.
    fxn()
    # Verify some things
    assert len(w) == 1
    assert issubclass(w[-1].category, DeprecationWarning)
    assert "deprecated" in str(w[-1].message)
Run Code Online (Sandbox Code Playgroud)

(B) 将警告升级为错误

如果警告以前没有出现过,因此已在警告注册表中注册,那么您可以设置警告来引发异常并捕获它。

import warnings


def fxn():
    warnings.warn("deprecated", DeprecationWarning)


if __name__ == '__main__':
    warnings.simplefilter("error", DeprecationWarning)

    try:
        fxn()
    except DeprecationWarning:
        print "Pass"
    else:
        print "Fail"
    finally:
        warnings.simplefilter("default", DeprecationWarning)
Run Code Online (Sandbox Code Playgroud)


Wil*_*hen 4

这不是最优雅的方法,但它对我有用:

from warnings import warn

class Foo(object):
    """
    Instantiating Foo always gives a warning:

    >>> import sys; sys.stderr = sys.stdout
    >>> foo = Foo() # doctest:+ELLIPSIS
    /.../testdocs.py:14: UserWarning: Boo!
      warn("Boo!", UserWarning)
    """

    def __init__(self):
        warn("Boo!", UserWarning)

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

不过,这可能在 Windows 上不起作用,因为 UserWarning 输出中报告的路径必须以斜杠开头,就像我编写此测试的方式一样。您也许能够找出 ELLIPSIS 指令的一些更好的咒语,但我不能。