14

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)

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.

警告は印刷されているようですが、doctestによってキャプチャまたは通知されていません。sys.stderrこれは、の代わりに警告が出力されるためだと思いますsys.stdoutsys.stderr = sys.stdoutしかし、これは、モジュールの最後で言った場合でも発生します。

では、doctestsを使用して警告をテストする方法はありますか?ドキュメントやGoogle検索で、この方法についての言及は見つかりません。

4

7 に答える 7

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()

ただし、UserWarningの出力で報告されるパスは、このテストを作成した方法でスラッシュで開始する必要があるため、これはおそらくWindowsでは機能しません。ELLIPSISディレクティブのより良い呪文を理解できるかもしれませんが、私は理解できませんでした。

于 2010-03-19T16:52:01.793 に答える
4

Python ドキュメントのTesting Warningsセクションは、このトピック専用です。ただし、要約すると、次の 2 つのオプションがあります。

(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)

(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)
于 2010-03-20T20:39:50.027 に答える
2

あなたが直面している問題は、をwarnings.warn()呼び出すことです。warnings.showwarning()これは、結果をファイルに書き込みwarnings.formatwarning()、デフォルトでは。になりsys.stderrます。

http://docs.python.org/library/warnings.html#warnings.showwarningを参照してください)

Python 2.6を使用している場合は、コンテキストマネージャーを使用して、代わりに書き込みwarnings.catch_warnings()の実装を一時的に置き換えるなど、警告の処理方法を簡単に変更できます。それがこのようなことを処理する正しい方法です。warnings.showwarning()sys.stdout

http://docs.python.org/library/warnings.html#available-context-managersを参照してください)

sys.stderrすばやく汚いハックが必要な場合は、次の場所にリダイレクトするデコレータをまとめてsys.stdoutください。

def stderr_to_stdout(func):
    def wrapper(*args):
        stderr_bak = sys.stderr
        sys.stderr = sys.stdout
        try:
            return func(*args)
        finally:
            sys.stderr = stderr_bak
    return wrapper

次に、doctestで装飾された関数を呼び出すことができます。

from warnings import warn
from utils import stderr_to_stdout

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

    >>> @stderr_to_stdout
    ... def make_me_a_foo():
    ...     Foo()
    ...
    >>> make_me_a_foo()
    testdocs.py:18: UserWarning: 
      warn("Boo!", UserWarning)
    >>>
    """ 
    def __init__(self):
        warn("Boo!", UserWarning)

合格:

$ python -m doctest testdocs.py -v
Trying:
    @stderr_to_stdout
    def make_me_a_foo():
        Foo()
Expecting nothing
ok
Trying:
    make_me_a_foo()
Expecting:
    testdocs.py:18: UserWarning: Boo!
      warn("Boo!", UserWarning)
ok
[...]
2 passed and 0 failed.
于 2010-03-19T16:44:45.753 に答える
0

docsは、doctestを実行するときに合格-Wdして、常に警告をトリガーできることを示唆しています。

于 2010-03-10T16:39:49.293 に答える
0

おそらく、面倒な部分をモック(パッチ プリント!) してみるとよいでしょう。これにより docstring が煩雑になることは認めますが、試してみる価値はあります。

現在の構文を維持しながらそのアプローチを使用したい場合は、不足しているコードを生成し、修正されたテストを実行する doctest のカスタム ラッパーを実装してみてください。可能であれば、それはおそらく避けるのが最善です。

または、完全にカスタムの doctest ランナーを作成することもできますが、これは避けたほうがよいでしょう。:)

于 2010-03-19T08:15:56.983 に答える
-1

これは、doctest がすべてのテストに適しているわけではない理由の一例です。docstrings にインラインの例があり、それらをテストする必要がある場合、それは 1 つのことですが、文字列の一致では最適ではない、テストしたい動作があることを発見しました。また、すべてのテスト メカニクスでドキュメント文字列をごちゃごちゃにする必要がない場合もあります。

于 2010-03-21T19:16:30.230 に答える