私は次のような関数のテストを書いています:
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モジュールで出力をキャッチするにはどうすればよいですか?
これを使用して context manager 出力をキャプチャします。一時的にsys.stdout
を置き換えることにより、最終的に他の回答のいくつかと同じ手法を使用します。コンテキストマネージャーは、すべての簿記を1つの関数にラップするため、コンテキストマネージャーを好みます。
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番目のキャプチャブロックを設定できます。これは、セットアップ関数およびティアダウン関数を使用して不可能です。 try-finallyブロックを手動で書き込む場合は冗長です。この機能は、テストの目的が、事前に計算された値ではなく、相互に関連する2つの関数の結果を比較することである場合に役立ちました。
これを本当に行いたい場合は、テスト中に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!'
バージョン2.7以降、sys.stdout
を再割り当てする必要はなくなりました。これは buffer
flag で提供されます。さらに、nosetestのデフォルトの動作です。
バッファリングされていないコンテキストで失敗するサンプルを次に示します。
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
、--buffer
、またはunittest.main
オプションでバッファを設定できます。反対は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)
これらの答えの多くは、あなたがfrom StringIO import StringIO
in Python 3.これは、@ naxaのコメントとPython Cookbook。
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')
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!'
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')
テストを書くと、コードを書くためのより良い方法が示されます。シェーンの答えと同様に、これを見るもう一つの方法を提案したいと思います。あなたのプログラムが特定の文字列を出力したことを本当に主張したいのですか、それとも特定の文字列を出力用に構築したのですか?これは、おそらくPython print
ステートメントが正しく機能すると仮定できるため、テストが容易になります。
def foo_msg():
return 'hello world'
def foo():
print foo_msg()
次に、テストは非常に簡単です。
def test_foo_msg():
assert 'hello world' == foo_msg()
もちろん、プログラムの実際の出力をテストする必要がある場合は、お気軽に無視してください。 :)
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()
または、pytest
の使用を検討してください。stdoutとstderrをアサートするためのサポートが組み込まれています。 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"