Python: 为控制台打印编写单元测试
Python: Write unittest for console print
函数 foo
打印到控制台。我想测试控制台打印。我怎样才能在 python 中做到这一点?
需要测试此功能,没有 return 声明:
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)
您可以通过暂时将 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。 Python3略有不同:
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()
这个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
arg 由 unittest.mock.patch
装饰器自动传递给 assert_stdout
方法。
一个通用的TestStdout
class,可能是mixin,原则上可以从上面推导出来。
对于那些使用 Python ≥3.4, contextlib.redirect_stdout
also exists, but it seems to serve no benefit over unittest.mock.patch
.
如果您碰巧使用 pytest
,它具有内置的输出捕获功能。示例(pytest
式测试):
def eggs():
print('eggs')
def test_spam(capsys):
eggs()
captured = capsys.readouterr()
assert captured.out == 'eggs\n'
您也可以将它与 unittest
测试 classes 一起使用,尽管您需要将夹具对象传递到测试中 class,例如通过自动夹具:
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)
你也可以使用如下所示的mock包,示例来自
https://realpython.com/lessons/mocking-print-unit-tests.
from mock import patch
def greet(name):
print('Hello ', name)
@patch('builtins.print')
def test_greet(mock_print):
# The actual test
greet('John')
mock_print.assert_called_with('Hello ', 'John')
greet('Eric')
mock_print.assert_called_with('Hello ', 'Eric')
@Acumenus 的 说:
It also uses a reusable helper method assert_stdout, although this helper is specific to the function being tested.
粗体部分似乎是一个很大的缺点,因此我会改为执行以下操作:
# extend unittest.TestCase with new functionality
class TestCase(unittest.TestCase):
def assertStdout(self, expected_output):
return _AssertStdoutContext(self, expected_output)
# as a bonus, this syntactical sugar becomes possible:
def assertPrints(self, *expected_output):
expected_output = "\n".join(expected_output) + "\n"
return _AssertStdoutContext(self, expected_output)
class _AssertStdoutContext:
def __init__(self, testcase, expected):
self.testcase = testcase
self.expected = expected
self.captured = io.StringIO()
def __enter__(self):
sys.stdout = self.captured
return self
def __exit__(self, exc_type, exc_value, tb):
sys.stdout = sys.__stdout__
captured = self.captured.getvalue()
self.testcase.assertEqual(captured, self.expected)
这允许更好、更可重用:
# in a specific test case, the new method(s) can be used
class TestPrint(TestCase):
def test_print1(self):
with self.assertStdout("test\n"):
print("test")
通过使用直接的上下文管理器。 (可能还需要将 "\n"
附加到 expected_output
,因为 print()
默认添加换行符。请参见下一个示例...)
此外,这个非常好的变体(打印任意数量!)
def test_print2(self):
with self.assertPrints("test1", "test2"):
print("test1")
print("test2")
现在可以了。
函数 foo
打印到控制台。我想测试控制台打印。我怎样才能在 python 中做到这一点?
需要测试此功能,没有 return 声明:
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)
您可以通过暂时将 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。 Python3略有不同:
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()
这个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
arg 由 unittest.mock.patch
装饰器自动传递给 assert_stdout
方法。
一个通用的TestStdout
class,可能是mixin,原则上可以从上面推导出来。
对于那些使用 Python ≥3.4, contextlib.redirect_stdout
also exists, but it seems to serve no benefit over unittest.mock.patch
.
如果您碰巧使用 pytest
,它具有内置的输出捕获功能。示例(pytest
式测试):
def eggs():
print('eggs')
def test_spam(capsys):
eggs()
captured = capsys.readouterr()
assert captured.out == 'eggs\n'
您也可以将它与 unittest
测试 classes 一起使用,尽管您需要将夹具对象传递到测试中 class,例如通过自动夹具:
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)
你也可以使用如下所示的mock包,示例来自 https://realpython.com/lessons/mocking-print-unit-tests.
from mock import patch
def greet(name):
print('Hello ', name)
@patch('builtins.print')
def test_greet(mock_print):
# The actual test
greet('John')
mock_print.assert_called_with('Hello ', 'John')
greet('Eric')
mock_print.assert_called_with('Hello ', 'Eric')
@Acumenus 的
It also uses a reusable helper method assert_stdout, although this helper is specific to the function being tested.
粗体部分似乎是一个很大的缺点,因此我会改为执行以下操作:
# extend unittest.TestCase with new functionality
class TestCase(unittest.TestCase):
def assertStdout(self, expected_output):
return _AssertStdoutContext(self, expected_output)
# as a bonus, this syntactical sugar becomes possible:
def assertPrints(self, *expected_output):
expected_output = "\n".join(expected_output) + "\n"
return _AssertStdoutContext(self, expected_output)
class _AssertStdoutContext:
def __init__(self, testcase, expected):
self.testcase = testcase
self.expected = expected
self.captured = io.StringIO()
def __enter__(self):
sys.stdout = self.captured
return self
def __exit__(self, exc_type, exc_value, tb):
sys.stdout = sys.__stdout__
captured = self.captured.getvalue()
self.testcase.assertEqual(captured, self.expected)
这允许更好、更可重用:
# in a specific test case, the new method(s) can be used
class TestPrint(TestCase):
def test_print1(self):
with self.assertStdout("test\n"):
print("test")
通过使用直接的上下文管理器。 (可能还需要将 "\n"
附加到 expected_output
,因为 print()
默认添加换行符。请参见下一个示例...)
此外,这个非常好的变体(打印任意数量!)
def test_print2(self):
with self.assertPrints("test1", "test2"):
print("test1")
print("test2")
现在可以了。