Как утверждать вывод с помощью nosetest/unittest в python?
Я пишу тесты для функции вроде следующей:
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 с параметром -s, тест завершится с ошибкой. Как я могу поймать вывод с помощью unittest или носового модуля?
Ответы
Ответ 1
Я использую этот менеджер контекста для вывода вывода. В конечном итоге он использует ту же технику, что и некоторые другие ответы, временно заменяя sys.stdout
. Я предпочитаю диспетчер контекста, потому что он переносит всю бухгалтерию в одну функцию, поэтому мне не нужно перезаписывать код try-finally, и мне не нужно писать функции настройки и разрыва только для этого.
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
, мы можем настроить второй блок захвата в той же функции, что и первый, что невозможно с помощью функций настройки и разрыва, и получает при написании блоков 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!'
Ответ 3
Начиная с версии 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)
Ответ 4
Я только изучаю Python и обнаружил, что борюсь с аналогичной проблемой с предыдущей версией с модульными тестами для методов с выходом. Мой пропущенный unit test для модуля 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')
Ответ 5
Многие из этих ответов не помогли мне, потому что вы не можете from StringIO import StringIO
в Python 3. Здесь минимальный рабочий фрагмент, основанный на комментарии @naxa и Cookbook 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')
Ответ 6
Написание тестов часто показывает нам лучший способ написать наш код. Как и ответ Шейна, я хотел бы предложить еще один способ взглянуть на это. Вы действительно хотите утверждать, что ваша программа вывела определенную строку или просто построила определенную строку для вывода? Это становится легче проверить, так как мы можем предположить, что оператор Python print
выполняет свою работу правильно.
def foo_msg():
return 'hello world'
def foo():
print foo_msg()
Тогда ваш тест очень прост:
def test_foo_msg():
assert 'hello world' == foo_msg()
Конечно, если у вас действительно есть необходимость протестировать фактическую производительность вашей программы, тогда не стесняйтесь игнорировать.:)
Ответ 7
В 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!'
Ответ 8
Или рассмотрите возможность использования pytest, он имеет встроенную поддержку для утверждения stdout и stderr. См. docs
Ответ 9
На основании ответа Роба Кеннеди я написал версию диспетчера контекстов на основе классов для буферизации вывода.
Использование:
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()