Mock Python's built in print function
Question:
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?
Answers:
First, the module is called __builtins__
and you don’t need to import it.
Now, in Python 2 print
is a keyword so you can’t use it as an attribute name directly. You can use setattr
/getattr
to workaround it:
getattr(__builtins__, "print")
Another option is to use from __future__ import print_function
which changes how Python parses the module to Python 3 syntax.
print
is a keyword in python 2.x, using it as attribute raises a SyntaxError. You can avoid that by using from __future__ import print_function
in the beginning of the file.
Note: you can’t simply use setattr
, because the print function you modified doesn’t get invoked unless the print
statement is disabled.
Edit: you also need to from __future__ import print_function
in every file you want your modified print
function to be used, or it will be masked by the print
statement.
If you want to stick with the print
statement from 2.x as opposed to the print()
function from 2.x, you could mock your sys.stdout
instead.
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.
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 Writable
with 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.
After that, you have a new class which can be instantiated with the “old stdout” as argument and can replace sys.stdout
after that.
As lcq says, print is 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:
print "Hi."
turning into:
<MagicMock id='49489360'> "Hi."
MagicMock objects cannot be accessed this way, so you would get a syntax error.
So… Yeah. You can only mock the Python3 print function or sys.stdout.
My version.
In the tested program(ex: 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')
]
)
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')]
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.html and it is:
>>> from StringIO import StringIO
>>> def foo():
... print 'Something'
...
>>> @patch('sys.stdout', new_callable=StringIO)
... def test(mock_stdout):
... foo()
... assert mock_stdout.getvalue() == 'Somethingn'
...
>>> test()
Of course you can use also following assertion:
self.assertEqual("Somethingn", mock_stdout.getvalue())
I have checked this solution in my unittests and it is working as expected. Hope this helps somebody. Cheers!
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.
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, '1n2n')
This is a much simpler Python 3 solution — it’s easier to use unittest.mock
directly on the builtin print
function, rather than fiddling around with 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()]
This is a v3 version of @KC’s answer.
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.
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 == 'Somethingn'
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.
#with patch('sys.stdout', new_callable=StringIO) as buffer:
if True:
foo()
...
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!")
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?
First, the module is called __builtins__
and you don’t need to import it.
Now, in Python 2 print
is a keyword so you can’t use it as an attribute name directly. You can use setattr
/getattr
to workaround it:
getattr(__builtins__, "print")
Another option is to use from __future__ import print_function
which changes how Python parses the module to Python 3 syntax.
print
is a keyword in python 2.x, using it as attribute raises a SyntaxError. You can avoid that by using from __future__ import print_function
in the beginning of the file.
Note: you can’t simply use setattr
, because the print function you modified doesn’t get invoked unless the print
statement is disabled.
Edit: you also need to from __future__ import print_function
in every file you want your modified print
function to be used, or it will be masked by the print
statement.
If you want to stick with the print
statement from 2.x as opposed to the print()
function from 2.x, you could mock your sys.stdout
instead.
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.
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 Writable
with 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.
After that, you have a new class which can be instantiated with the “old stdout” as argument and can replace sys.stdout
after that.
As lcq says, print is 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:
print "Hi."
turning into:
<MagicMock id='49489360'> "Hi."
MagicMock objects cannot be accessed this way, so you would get a syntax error.
So… Yeah. You can only mock the Python3 print function or sys.stdout.
My version.
In the tested program(ex: 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')
]
)
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')]
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.html and it is:
>>> from StringIO import StringIO
>>> def foo():
... print 'Something'
...
>>> @patch('sys.stdout', new_callable=StringIO)
... def test(mock_stdout):
... foo()
... assert mock_stdout.getvalue() == 'Somethingn'
...
>>> test()
Of course you can use also following assertion:
self.assertEqual("Somethingn", mock_stdout.getvalue())
I have checked this solution in my unittests and it is working as expected. Hope this helps somebody. Cheers!
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.
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, '1n2n')
This is a much simpler Python 3 solution — it’s easier to use unittest.mock
directly on the builtin print
function, rather than fiddling around with 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()]
This is a v3 version of @KC’s answer.
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.
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 == 'Somethingn'
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.
#with patch('sys.stdout', new_callable=StringIO) as buffer:
if True:
foo()
...
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!")