2010-11-18 6 views
75

次のような関数のテストを書いています。nosetest/unittestで出力をアサートする方法は?

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!' 

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

+1

[ 'mock.patchと( 'sys.stdoutの'、new_callable = StringIO.StringIO):'](HTTP: //www.voidspace.org.uk/python/mock/patch.html#patch)https://pypi.python.org/pypi/mock/ – n611x007

答えて

52

これを本当に行いたい場合は、テスト期間中に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 

私はこのコードを書いていた場合は、しかし、私はfoo関数にオプションのoutパラメータを渡すことを好むだろう。

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!' 
+10

注:Python 3.xでは、 'StringIO'クラスは'io'モジュールからインポートします。 'from io import StringIO'は、Python 2.6以降で動作します。 –

+2

Python 2で 'from io import StringIO'を使うと、' TypeError:unicode引数が必要です。印刷時に 'str'が得られます。 – matiasg

+8

クイックメモ:Python 3.4では、[contextlib.redirect_stdout](https://docs.python.org/3.4/library/contextlib.html#contextlib.redirect_stdout)コンテキストマネージャを使用して、例外セーフ: 'redirect_stdout(out):' – Lucretiel

9

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

def foo_msg(): 
    return 'hello world' 

def foo(): 
    print foo_msg() 

次に、あなたのテストは非常に簡単です:もちろん

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

、あなたが本当にあなたのプログラムの実際の出力をテストする必要がある場合は、無視して自由に感じます。 :)

+0

しかし、この場合fooはテストされません...おそらくそれは問題です –

+4

テスト純粋主義者の観点からはおそらく問題です。実用的な見地から、 'foo()'がprintステートメントを呼び出す以外に何もしないなら、おそらく*問題ではないでしょう。 –

12

私はちょうど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') 
+2

'sys.stdout.getvalue()。strip()'を実行し、 '\ n'と比較して不正行為をしたくない場合があります:) – Silviu

43

バージョン2.7以来、あなたは、これはbuffer flagを介して提供され、sys.stdoutを再割り当てするもう必要はありません。さらに、nosetestのデフォルト動作です。あなたはunit2コマンドラインフラグ-b--bufferを介して、またはunittest.mainオプションでバッファリング設定でき

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!') 

はここで非バッファリングのコンテキストに失敗したサンプルです。 逆はnosetestフラグ--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) 
+0

この使用法は文書化する必要があります。とてもかっこいい。 – 2rs2ts

+0

これは['--nocapture']と相互作用することに注意してください(https://stackoverflow.com/questions/5975194/nosetests-is-capturing-the-output-of-my-print-statements-how-to-circumvent-この);特に、このフラグが設定されている場合、バッファモードは無効になります。したがって、端末上の出力を見ることができるか、または出力が期待どおりであるかをテストすることができます。 – ijoseph

+0

これは、ipdb.set_trace()のようなものを使用するとデバッグが非常に困難になるため、これをテストごとにオンまたはオフにすることはできますか? – Lqueryvg

75

私はこの出力をキャプチャするのにcontext managerを使用します。それは最終的にはsys.stdoutを一時的に置き換えることによって、他のいくつかの回答と同じテクニックを使用します。私はコンテクストマネージャーを好んでいます。なぜなら、すべての簿記を単一の関数にまとめているから、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ブロックを出る時に復元されるので

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!') 

さらに、我々は最初のものと同じ機能で第二キャプチャブロックを設定することができこれはsetupとteardown関数を使っては不可能で、try-finallyブロックを手動で書くときにはwordyになります。その能力は、テストの目的が、いくつかの事前計算された値ではなく、2つの関数の結果を互いに比較することだった場合に便利でした。

+0

これは[pep8radius](https://github.com/hayd/pep8radius)で本当にうまくいっています。最近、私はこれをもう一度使って、 'TypeError:unicode argument expected、 'str'(print(str/unicode)に渡される型は無関係です)を出力するときに次のエラーを受け取りました。 –

+2

Hmmm Python 2では、 'from io import BytesIOをStringIO'、Python 3を' from io import StringIO'としたいかもしれません。私が思っているテストで問題を修正したようだ。 –

+3

Ooop、ちょうど終了するまで、非常に多くのメッセージにお詫び申し上げます。これを見つけた人々を明確にするために:python3 use io.StringIO、python 2 StringIO.StringIOを使用してください!再度、感謝します! –

1

pytestを使用することを検討してください。stdoutとstderrをアサートするための組み込みサポートがあります。あなたがここに@をNAXAさんのコメントやPythonクックブックに基づいて最小加工スニペットだないfrom StringIO import StringIOのPython 3の可能性があるためdocs

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" 
+0

良い。リンクが消えてコンテンツが変わる可能性があるため、最小限の例を含めることはできますか? – KobeJohn

10

を参照してくださいこれらの答えの多くは私のために失敗しました。

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') 
11

のpython 3.5では、あなたはcontextlib.redirect_stdout()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!' 
2

Rob Kennedyの答えに基づいて、出力をバッファするためのクラスベースのバージョンのコンテキストマネージャーを作成しました。

使用法のようなある:

with OutputBuffer() as bf: 
    print('hello world') 
assert bf.out == 'hello world\n' 

ここ実装の:

from io import StringIO 
import sys 


class OutputBuffer(object): 

    def __init__(self): 
     self.stdout = StringIO() 
     self.stderr = StringIO() 

    def __enter__(self): 
     self.original_stdout, self.original_stderr = sys.stdout, sys.stderr 
     sys.stdout, sys.stderr = self.stdout, self.stderr 
     return self 

    def __exit__(self, exception_type, exception, traceback): 
     sys.stdout, sys.stderr = self.original_stdout, self.original_stderr 

    @property 
    def out(self): 
     return self.stdout.getvalue() 

    @property 
    def err(self): 
     return self.stderr.getvalue() 
関連する問題