我试过
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
有没有办法做到这一点?
答案 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')