Python: написать юнит-тест для консольной печати
Функция foo
печатает на консоли. Я хочу протестировать консольную печать. Как я могу достичь этого в python?
Необходимо протестировать эту функцию, не имеет оператора возврата:
def foo(inStr):
print "hi"+inStr
Мой тест:
def test_foo():
cmdProcess = subprocess.Popen(foo("test"), stdout=subprocess.PIPE)
cmdOut = cmdProcess.communicate()[0]
self.assertEquals("hitest", cmdOut)
Ответы
Ответ 1
Вы можете легко захватить стандартный вывод, просто временно перенаправив sys.stdout
на объект StringIO
, следующим образом:
import StringIO
import sys
def foo(inStr):
print "hi"+inStr
def test_foo():
capturedOutput = StringIO.StringIO() # Create StringIO object
sys.stdout = capturedOutput # and redirect stdout.
foo('test') # Call unchanged function.
sys.stdout = sys.__stdout__ # Reset redirect.
print 'Captured', capturedOutput.getvalue() # Now works as before.
test_foo()
Выход этой программы:
Captured hitest
показывая, что перенаправление успешно захватило выходные данные и что вы смогли восстановить поток вывода до того, что было до того, как вы начали захват.
Обратите внимание, что приведенный выше код для Python 2.7, как показывает вопрос. Python 3 немного отличается:
import io
import sys
def foo(inStr):
print ("hi"+inStr)
def test_foo():
capturedOutput = io.StringIO() # Create StringIO object
sys.stdout = capturedOutput # and redirect stdout.
foo('test') # Call function.
sys.stdout = sys.__stdout__ # Reset redirect.
print ('Captured', capturedOutput.getvalue()) # Now works as before.
test_foo()
Ответ 2
Этот ответ Python 3 использует unittest.mock
. Он также использует многократно используемый вспомогательный метод assert_stdout
, хотя этот вспомогательный параметр специфичен для тестируемой функции.
import io
import unittest
import unittest.mock
from .solution import fizzbuzz
class TestFizzBuzz(unittest.TestCase):
@unittest.mock.patch('sys.stdout', new_callable=io.StringIO)
def assert_stdout(self, n, expected_output, mock_stdout):
fizzbuzz(n)
self.assertEqual(mock_stdout.getvalue(), expected_output)
def test_only_numbers(self):
self.assert_stdout(2, '1\n2\n')
Обратите внимание, что mock_stdout
автоматически unittest.mock.patch
декоратором assert_stdout
метод assert_stdout
.
TestStdout
класс TestStdout
, возможно, mixin, в принципе может быть получен из вышеупомянутого.
Для тех, кто использует Python ≥3.4, также существует contextlib.redirect_stdout
, но, похоже, он не unittest.mock.patch
никаких преимуществ по сравнению с unittest.mock.patch
.
Ответ 3
Если вы используете pytest
, он имеет встроенный захват вывода. Пример (pytest
-style):
def eggs():
print('eggs')
def test_spam(capsys):
eggs()
captured = capsys.readouterr()
assert captured.out == 'eggs\n'
Вы также можете использовать его с тестовыми классами unittest
, хотя вам необходимо вставить объект фикстуры в тестовый класс, например, через фиксацию autouse:
import unittest
import pytest
class TestSpam(unittest.TestCase):
@pytest.fixture(autouse=True)
def _pass_fixtures(self, capsys):
self.capsys = capsys
def test_eggs(self):
eggs()
captured = self.capsys.readouterr()
self.assertEqual('eggs\n', captured.out)
Проверьте Доступ к захваченному выводу из тестовой функции для получения дополнительной информации.