Python:为控制台打印编写unittest

时间:2015-11-17 21:46:16

标签: 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("hitest", cmdOut)

5 个答案:

答案 0 :(得分:31)

您可以通过暂时将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

该程序的输出是:

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 2.7,如问题所示。 Python 3略有不同:

runat="server"

答案 1 :(得分:18)

此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 arg传递给assert_stdout方法。

通用TestStdout类,可能是mixin,原则上可以从上面得出。

对于使用Python≥3.4的人,contextlib.redirect_stdout也存在,但它似乎没有unittest.mock.patch的好处。

答案 2 :(得分:1)

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

def eggs():
    print('eggs')


def test_spam(capsys):
    eggs()
    captured = capsys.readouterr()
    assert captured.out == 'eggs\n'

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

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)

查看Accessing captured output from a test function了解更多信息。

答案 3 :(得分:1)

您还可以使用如下所示的模拟程序包,这是来自以下示例的示例 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')

答案 4 :(得分:0)

@Acumenus 的 answer 说:

<块引用>

它还使用了一个可重用的辅助方法 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" 附加到 expected_output,因为 print() 默认添加换行符。请参阅下一个示例...)

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

    def test_print2(self):
        with self.assertPrints("test1", "test2"):
            print("test1")
            print("test2")

现在有可能。