122

次のような関数のテストを書いています:

def foo():
    print 'hello world!'

したがって、この関数をテストする場合、コードは次のようになります。

import sys
from foomodule import foo
def test_foo():
    foo()
    output = sys.stdout.getline().strip() # because stdout is an StringIO instance
    assert output == 'hello world!'

しかし、-s パラメータを付けて NOSETESTS を実行すると、テストがクラッシュします。unittestまたはnoseモジュールで出力をキャッチするにはどうすればよいですか?

4

13 に答える 13

131

このコンテキスト マネージャーを使用して、出力をキャプチャします。最終的には、一時的に置き換えることにより、他のいくつかの回答と同じ手法を使用しますsys.stdout。すべてのブックキーピングを 1 つの関数にラップするので、私はコンテキスト マネージャーを好みます。したがって、try-finally コードを書き直す必要がなく、このためだけにセットアップ関数とティアダウン関数を記述する必要もありません。

import sys
from contextlib import contextmanager
from StringIO import StringIO

@contextmanager
def captured_output():
    new_out, new_err = StringIO(), StringIO()
    old_out, old_err = sys.stdout, sys.stderr
    try:
        sys.stdout, sys.stderr = new_out, new_err
        yield sys.stdout, sys.stderr
    finally:
        sys.stdout, sys.stderr = old_out, old_err

次のように使用します。

with captured_output() as (out, err):
    foo()
# This can go inside or outside the `with` block
output = out.getvalue().strip()
self.assertEqual(output, 'hello world!')

さらに、ブロックを終了すると元の出力状態が復元されるためwith、最初のキャプチャ ブロックと同じ関数で 2 番目のキャプチャ ブロックをセットアップできます。手動でブロックします。この機能は、テストの目的が、事前に計算された値ではなく、2 つの関数の結果を相互に比較することであった場合に役立ちました。

于 2013-07-31T22:16:00.643 に答える
65

本当にこれを行いたい場合は、テスト中に sys.stdout を再割り当てできます。

def test_foo():
    import sys
    from foomodule import foo
    from StringIO import StringIO

    saved_stdout = sys.stdout
    try:
        out = StringIO()
        sys.stdout = out
        foo()
        output = out.getvalue().strip()
        assert output == 'hello world!'
    finally:
        sys.stdout = saved_stdout

ただし、このコードを作成する場合は、オプションのoutパラメーターをfoo関数に渡すことをお勧めします。

def foo(out=sys.stdout):
    out.write("hello, world!")

次に、テストははるかに簡単です。

def test_foo():
    from foomodule import foo
    from StringIO import StringIO

    out = StringIO()
    foo(out=out)
    output = out.getvalue().strip()
    assert output == 'hello world!'
于 2010-11-18T22:28:52.870 に答える
48

バージョン 2.7 以降、 reassign を行う必要はなくなりました。これはflagsys.stdoutによって提供されます。さらに、それはnosetestのデフォルトの動作です。buffer

バッファリングされていないコンテキストで失敗するサンプルを次に示します。

import sys
import unittest

def foo():
    print 'hello world!'

class Case(unittest.TestCase):
    def test_foo(self):
        foo()
        if not hasattr(sys.stdout, "getvalue"):
            self.fail("need to run in buffered mode")
        output = sys.stdout.getvalue().strip() # because stdout is an StringIO instance
        self.assertEquals(output,'hello world!')

unit2コマンドラインフラグまたはオプション-bでバッファを設定できます。逆は、flagを介して実現されます。--bufferunittest.mainnosetest--nocapture

if __name__=="__main__":   
    assert not hasattr(sys.stdout, "getvalue")
    unittest.main(module=__name__, buffer=True, exit=False)
    #.
    #----------------------------------------------------------------------
    #Ran 1 test in 0.000s
    #
    #OK
    assert not hasattr(sys.stdout, "getvalue")

    unittest.main(module=__name__, buffer=False)
    #hello world!
    #F
    #======================================================================
    #FAIL: test_foo (__main__.Case)
    #----------------------------------------------------------------------
    #Traceback (most recent call last):
    #  File "test_stdout.py", line 15, in test_foo
    #    self.fail("need to run in buffered mode")
    #AssertionError: need to run in buffered mode
    #
    #----------------------------------------------------------------------
    #Ran 1 test in 0.002s
    #
    #FAILED (failures=1)
于 2012-10-02T00:29:47.617 に答える
39

Python 3ではできないため、これらの回答の多くは失敗しましたfrom StringIO import StringIO。@naxaのコメントとPythonクックブックに基づく最小限の作業スニペットを次に示します。

from io import StringIO
from unittest.mock import patch

with patch('sys.stdout', new=StringIO()) as fakeOutput:
    print('hello world')
    self.assertEqual(fakeOutput.getvalue().strip(), 'hello world')
于 2015-07-08T00:07:54.700 に答える
30

contextlib.redirect_stdout()Python 3.5 では、 とを使用できますStringIO()。ここにあなたのコードへの変更があります

import contextlib
from io import StringIO
from foomodule import foo

def test_foo():
    temp_stdout = StringIO()
    with contextlib.redirect_stdout(temp_stdout):
        foo()
    output = temp_stdout.getvalue().strip()
    assert output == 'hello world!'
于 2016-09-04T19:20:14.507 に答える
17

私はPythonを学んでいるだけで、出力を伴うメソッドの単体テストで上記の問題と同様の問題に苦しんでいることに気づきました。上記の foo モジュールの単体テストに合格すると、次のようになります。

import sys
import unittest
from foo import foo
from StringIO import StringIO

class FooTest (unittest.TestCase):
    def setUp(self):
        self.held, sys.stdout = sys.stdout, StringIO()

    def test_foo(self):
        foo()
        self.assertEqual(sys.stdout.getvalue(),'hello world!\n')
于 2011-10-04T22:33:04.990 に答える
10

テストを書くことは、私たちのコードを書くためのより良い方法を私たちにしばしば示します。シェーンの答えと同様に、これをさらに別の見方で提案したいと思います。プログラムが特定の文字列を出力したことを本当に主張したいですか、それとも単に出力用に特定の文字列を作成したことを主張したいですか?printPythonステートメントが正しく機能すると想定できるため、これはテストが容易になります。

def foo_msg():
    return 'hello world'

def foo():
    print foo_msg()

次に、テストは非常に簡単です。

def test_foo_msg():
    assert 'hello world' == foo_msg()

もちろん、プログラムの実際の出力をテストする必要がある場合は、無視してかまいません。:)

于 2010-11-19T01:02:00.370 に答える
2

またはpytest、stdout と stderr をアサートするためのサポートが組み込まれている の使用を検討してください。ドキュメントを見る

def test_myoutput(capsys): # or use "capfd" for fd-level
    print("hello")
    captured = capsys.readouterr()
    assert captured.out == "hello\n"
    print("next")
    captured = capsys.readouterr()
    assert captured.out == "next\n"
于 2015-06-23T14:55:43.863 に答える