46

我试过了

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

有没有办法做到这一点?

4

11 回答 11

54

我知道已经有一个公认的答案,但是对于这个问题有一个更简单的解决方案 - 在 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())

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

于 2015-07-01T21:08:36.317 回答
29

unittest.mock这是一个更简单的 Python 3 解决方案——直接在内置函数上使用更容易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()]
于 2018-06-07T11:59:16.863 回答
18

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

注意:您不能简单地使用setattr,因为您修改的打印功能不会被调用,除非该print语句被禁用。

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

于 2012-10-21T15:13:04.523 回答
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

此类期望与获取打印数据的写入函数相结合。这个写入函数应该有两个参数:第一个带有“旧标准输出”的最后用于打印,另一个用于数据。

让我们来

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在装饰器函数的帮助下创建了一个子类,该函数将给定函数转换为新创建的类的方法,并放入给定函数来自的名称中。

之后,您有一个新类,可以使用“旧标准输出”作为参数进行实例化,然后可以替换sys.stdout

于 2012-10-21T19:45:53.987 回答
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')
            ]
        )
于 2014-03-26T12:23:22.810 回答
3
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!")
于 2020-08-18T14:50:35.793 回答
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')]
于 2014-10-23T13:42:29.743 回答
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()

警告:

在补丁期间,模拟 stdout 与 using 效果不佳pdb.set_trace()。我注释掉with...,添加if True:以保留缩进,调试我的脚本并在修复错误后放回批处理。

    #with patch('sys.stdout', new_callable=StringIO) as buffer:
    if True:
        foo()
    ...
于 2020-05-02T16:51:24.530 回答
0

首先,模块被调用__builtins__,你不需要导入它。

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

getattr(__builtins__, "print")

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

于 2012-10-21T15:04:10.023 回答
0

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

print "Hi."

转变为:

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

MagicMock 对象不能以这种方式访问​​,因此您会收到语法错误。

是的。您只能模拟 Python3 打印函数或 sys.stdout。

于 2013-01-03T02:38:39.177 回答
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')
于 2017-09-19T18:38:29.070 回答