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?

Asked By: aychedee

||

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.

Answered By: lqc

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.

Answered By: quantum

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.

Answered By: glglgl

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.

Answered By: dbn

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')
            ]
        )
Answered By: Chien-Wei Huang
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')]
Answered By: sgjurano

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!

Answered By: Krzysztof Czeronko

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')
Answered By: Asclepius

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()]
Answered By: Tom

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()
    ...
Answered By: JL Peyret
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!")
Answered By: Vlad Bezden