Python:编写控制台打印的unittest

Python:编写控制台打印的unittest,python,python-2.7,unit-testing,console,python-unittest,Python,Python 2.7,Unit Testing,Console,Python Unittest,函数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("hi

函数
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)

只需将
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
显示重定向成功捕获了输出,并且您能够将输出流恢复到开始捕获之前的状态


请注意,如问题所示,上面的代码是针对Python2.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()
这个Python3答案使用了。它还使用一个可重用的助手方法
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\u stdout
arg由
unittest.mock.patch
decorator自动传递给
assert\u stdout
方法

一个通用的
TestStdout
类,可能是一个mixin,原则上可以从上面得到

对于那些使用Python的人≥3.4也存在,但它似乎没有任何好处。

如果您碰巧使用,它有内置的输出捕获功能。示例(
pytest
样式测试):

您也可以将其与
unittest
测试类一起使用,尽管您需要将fixture对象传递到测试类中,例如通过自动使用fixture:

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)

查看更多信息。

您还可以使用模拟软件包,如下所示,这是

来自模拟导入修补程序的

姓名:
打印('Hello',name)
@修补程序('builtins.print')
def测试(模拟打印):
#实际测试
问候(‘约翰’)
mock\u print.assert\u使用('Hello','John')调用\u
问候(‘埃里克’)
mock_print.assert_用('Hello','Eric')调用_
的@acumones说明:

它还使用一个可重用的助手方法assert\stdout,尽管这个助手特定于被测试的函数。

粗体部分似乎是一个很大的缺点,因此我将执行以下操作:

# 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”
附加到
预期的\u输出
,因为
print()
默认情况下会添加换行符。请参见下一个示例…)

此外,这个非常好的变体(用于任意数量的打印!)


现在可以了。

复制tl;dr使用future或assert在替换的标准文件上将打印转换为内置函数我不想模拟任何东西。实际上,我的实际
foo
接受大约8个参数,并返回一个json。我还想测试一下这一点。在Python 3.7中,您现在在Python
3.8.6
中使用
import io
io.SocketIO()
,使用
import io
io.StringIO()
。对于
SocketIO
我得到了
属性错误:模块“io”没有属性“SocketIO”
@EnriqueRené:我不确定我是否理解您的评论。自从Python3部分在2017年添加以来,我在Python3的答案中有了
io.StringIO()
。没有任何版本提到过
SocketIO
@paxdiablo我的评论是关于上述@Alex评论的,据我所知,我也可以使用
SocketIO
。我尝试了这个建议,得到了
AttributeError
。由于您的类名为TestCase,我假设您正在对unittest.TestCase进行子类化以扩展它,并且def test_print(self)是TestPrintClass(TestCase)类的一部分,其中TestCase是您的扩展实现。对吗?--也许在这里说的是显而易见的,但这是我读代码时突然想到的绝对正确的东西。很抱歉,示例中缺少了
TestPrintClass
。我会加进去的!
# 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")
    def test_print2(self):
        with self.assertPrints("test1", "test2"):
            print("test1")
            print("test2")