模拟Python的内置打印功能

时间:2012-10-21 14:50:41

标签: python unit-testing mocking python-2.x

我试过

from mock import Mock
import __builtin__

__builtin__.print = Mock()

但这会引发语法错误。我也尝试过修补它

@patch('__builtin__.print')
def test_something_that_performs_lots_of_prints(self, mock_print):

    # assert stuff

有没有办法做到这一点?

11 个答案:

答案 0 :(得分:45)

我知道已经有一个已经接受的答案,但是对于这个问题有更简单的解决方案 - 在python 2.x中模拟打印。答案在模拟库教程中:http://www.voidspace.org.uk/python/mock/patch.html,它是:

>>> from StringIO import StringIO
>>> def foo():
...     print 'Something'
...
>>> @patch('sys.stdout', new_callable=StringIO)
... def test(mock_stdout):
...     foo()
...     assert mock_stdout.getvalue() == 'Something\n'
...
>>> test()

当然你也可以使用以下断言:

self.assertEqual("Something\n", mock_stdout.getvalue())

我已经在我的单元测试中检查了这个解决方案,它正在按预期工作。希望这有助于某人。干杯!

答案 1 :(得分:15)

print是python 2.x中的关键字,使用它作为属性引发SyntaxError。您可以在文件开头使用from __future__ import print_function来避免这种情况。

注意:您不能简单地使用setattr,因为除非print语句被禁用,否则不会调用您修改的print函数。

修改:您还需要在每个要使用修改后的from __future__ import print_function函数的文件中print,否则它将被print语句屏蔽。

答案 2 :(得分:9)

这是一个更简单的Python 3解决方案 - 直接在内置unittest.mock函数上使用print更容易,而不是摆弄sys.stdout

from unittest.mock import patch, call

@patch('builtins.print')
def test_print(mocked_print):
    print('foo')
    print()

    assert mocked_print.mock_calls == [call('foo'), call()]

答案 3 :(得分:3)

如果您想坚持使用2.x中的print语句而不是2.x中的print()语句,则可以改为模拟sys.stdout

写一个虚拟的“文件”,也许就是这样:

class Writable(object):
    """Class which has the capability to replace stdout."""
    newwrite = None
    def __init__(self, oldstdout, newwrite=None):
        self.oldstdout = oldstdout
        if newwrite is not None:
            self.newwrite = newwrite
    def write(self, data):
        self.newwrite(self.oldstdout, data)
    @classmethod
    def subclass(cls, writefunc):
        newcls = type('', (cls,),
            dict(write=lambda self, data: writefunc(self.oldstdout, data)
        return newcls

该类希望与获取打印数据的写入功能相结合。这个写入函数应该有2个参数:第一个用“old stdout”用于最后打印,另一个用于数据。

我们来看看

def mywrite(sink, data):
    sink.write(data.encode("hex"))

为此。

现在你可以做到

import sys
sys.stdout = Writable(sys.stdout, mywrite)

或者你可以做

@Writable.subclass
def mywritable(sink, data)
    sink.write(data.encode("hex"))

sys.stdout = mywritable(sys.stdout)

第二个版本有点棘手:它在装饰器函数的帮助下创建了Writable的子类,该函数将给定的函数转换为创建的新类的方法,并放入名称中给定的功能来自。

之后,你有了一个新类,可以用“old stdout”作为参数进行实例化,之后可以替换sys.stdout

答案 4 :(得分:3)

我的版本。

在测试程序中(例如:pp.py):

from __future__ import print_function

def my_func():
    print('hello')

在测试程序中:

def test_print(self):
    from pp import my_func
    from mock import call
    with mock.patch('__builtin__.print') as mock_print:
       my_func()
       mock_print.assert_has_calls(
            [
                call('hello')
            ]
        )

答案 5 :(得分:1)

import mock
import sys

mock_stdout = mock.Mock()
sys.stdout = mock_stdout
print 'Hello!'
sys.stdout = sys.__stdout__

print mock_stdout.mock_calls
[call.write('Hello!'), call.write('\n')]

答案 6 :(得分:1)

这是@KC答案的v3版本。

我不想模拟打印,因为我特别想查看整个输出,而不是检查单个调用,所以StringIO对我来说更有意义。

from io import StringIO
from unittest.mock import patch

def foo():
    print ('Something')

def test():
    with patch('sys.stdout', new_callable=StringIO) as buffer:
        foo()
    fake_stdout = buffer.getvalue()

    #print() is back!
    print(f"fake_stdout:{fake_stdout}")
    assert fake_stdout == 'Something\n'

test()

???警告:

在补丁期间,使用pdb.set_trace()时,嘲笑标准输出效果很差。我注释了with...,添加了if True:来保持缩进,调试了脚本,并在纠正错误后放回了批处理。

    #with patch('sys.stdout', new_callable=StringIO) as buffer:
    if True:
        foo()
    ...

答案 7 :(得分:1)

from unittest.mock import patch


def greet():
    print("Hello World")


@patch('builtins.print')
def test_greet(mock_print):
    greet()
    mock_print.assert_called_with("Hello World!")

答案 8 :(得分:0)

首先,该模块名为__builtins__,您无需导入该模块。

现在,在Python 2中print是一个关键字,因此您不能直接将其用作属性名称。您可以使用setattr / getattr来解决问题:

getattr(__builtins__, "print")

另一种选择是使用from __future__ import print_function来改变Python将模块解析为Python 3语法的方式。

答案 9 :(得分:0)

正如lcq所说,打印是一个关键字。所以,想想如果你在Python 2.7.3下修补/模拟打印成功的话会是什么意思。你会得到这样的代码:

print "Hi."

变成:

<MagicMock id='49489360'> "Hi."

无法以这种方式访问​​MagicMock对象,因此会出现语法错误。

所以......是的您只能模拟Python3 print function 或sys.stdout。

答案 10 :(得分:0)

这个Python 3示例基于Krzysztof的Python 2答案。它使用unittest.mock。它使用可重用的辅助方法来进行断言。

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')