模拟 Python 的内置打印功能

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12998908/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 12:22:52  来源:igfitidea点击:

Mock Python's built in print function

pythonunit-testingmockingpython-2.x

提问by aychedee

I've tried

我试过了

from mock import Mock
import __builtin__

__builtin__.print = Mock()

But that raises a syntax error. I've also tried patching it like so

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

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

    # assert stuff

Is there any way to do this?

有没有办法做到这一点?

采纳答案by quantum

printis a keyword in python 2.x, using it as attribute raises a SyntaxError. You can avoid that by using from __future__ import print_functionin the beginning of the file.

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

Note: you can't simply use setattr, because the print function you modified doesn't get invoked unless the printstatement is disabled.

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

Edit: you also need to from __future__ import print_functionin every file you want your modified printfunction to be used, or it will be masked by the printstatement.

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

回答by lqc

First, the module is called __builtins__and you don't need to import it.

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

Now, in Python 2 printis a keyword so you can't use it as an attribute name directly. You can use setattr/getattrto workaround it:

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

getattr(__builtins__, "print")

Another option is to use from __future__ import print_functionwhich changes how Python parses the module to Python 3 syntax.

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

回答by glglgl

If you want to stick with the printstatement from 2.x as opposed to the print()function from 2.x, you could mock your sys.stdoutinstead.

如果你想坚持使用print2.x 中的语句而不是 2.x 中的print()函数,你可以模拟你的sys.stdout

Write a dummy "file", perhaps in about this way:

写一个虚拟的“文件”,大概是这样的:

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

This class expects to be combined with a writing function which gets the printed data. This writing function is supposed to take 2 arguments: the first one with the "old stdout" to be used for printing at the end, and a further one for the data.

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

Let's take

让我们来

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

for that.

为了那个原因。

Now you can do

现在你可以做

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

or you can do

或者你可以做

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

sys.stdout = mywritable(sys.stdout)

The 2nd version is a bit trickier: it creates a subclass of the Writablewith the help of a decorator function which turns the given function into a method of the new class created instead and put into the name where the given function comes from.

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

After that, you have a new class which can be instantiated with the "old stdout" as argument and can replace sys.stdoutafter that.

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

回答by dbn

As lcq says, printis a keyword. So, think about what it would mean if you were actually successful in patching/mocking print under Python 2.7.3. You would have code like this:

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

print "Hi."

turning into:

转变为:

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

MagicMock objects cannot be accessed this way, so you would get a syntax error.

无法通过这种方式访问​​ MagicMock 对象,因此您会收到语法错误。

So... Yeah. You can only mock the Python3 print functionor sys.stdout.

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

回答by Chien-Wei Huang

My version.

我的版本。

In the tested program(ex: pp.py):

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

from __future__ import print_function

def my_func():
    print('hello')

In the test program:

在测试程序中:

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

回答by sgjurano

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

回答by Krzysztof Czeronko

I know that there is already an accepted answer but there is simpler solution for that problem - mocking the print in python 2.x. Answer is in the mock library tutorial: http://www.voidspace.org.uk/python/mock/patch.htmland it is:

我知道已经有一个可以接受的答案,但有一个更简单的解决方案 - 在 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()

Of course you can use also following assertion:

当然,您也可以使用以下断言:

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

I have checked this solution in my unittests and it is working as expected. Hope this helps somebody. Cheers!

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

回答by Acumenus

This Python 3 example builds upon the Python 2 answer by Krzysztof. It uses unittest.mock. It uses a reusable helper method for making the assertion.

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

回答by Tom

This is a much simpler Python 3 solution -- it's easier to use unittest.mockdirectly on the builtin printfunction, rather than fiddling around with sys.stdout:

这是一个更简单的 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()]

回答by JL Peyret

This is a v3 version of @KC's answer.

这是@KC 答案的 v3 版本。

I didn't want to mock print because I specifically wanted to see the output as a whole, not check out individual calls so StringIO makes more sense to me.

我不想模拟打印,因为我特别想查看整个输出,而不是查看单个调用,因此 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()

warning:

警告:

for the duration of the patch, mocking stdout plays badly with using pdb.set_trace(). I commented out with..., added if True:to keep the indentation, debugged my script and put back the batch once I fixed my error.

在补丁期间,模拟标准输出在使用pdb.set_trace(). 我注释掉with...,添加if True:以保持缩进,调试我的脚本并在我修复错误后放回批处理。

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