Locked learning resources

Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Locked learning resources

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Mocking print() in Unit Tests

It can be difficult to write unit tests for methods like print() that don’t return anything but have a side-effect of writing to the terminal. You want to ensure that what you expected to print to the terminal actually got printed to the terminal. The unittest.mock library can help you test functions that have calls to print():

Language: Python
def greet(name):
    print('Hello ', name)

from unittest.mock import patch

@patch('builtins.print')
def test_greet(mock_print):
    # The actual test
    greet('John')
    mock_print.assert_called_with('Hello ', 'John')
    greet('Eric')
    mock_print.assert_called_with('Hello ', 'Eric')

    # Showing what is in mock
    import sys
    sys.stdout.write(str( mock_print.call_args ) + '\n')
    sys.stdout.write(str( mock_print.call_args_list ) + '\n')

00:00 In the previous lesson, I introduced you to simple animation using the \r and \b escape sequences. In this lesson, I’m going to talk about how to mock print() if you’re testing code that has print() inside of it. For any non-trivial sized code, you should always build automated tests.

00:17 Automated tests help you make sure your code has good quality, it’s faster than testing by hand, and it helps prevent regressive defects. As you modify your code as you move along, you can always rerun your tests to make sure that you haven’t broken anything.

00:31 print() as a function has a side effect. Any function with side effects is more challenging to test. print()’s side effect is displaying the output.

00:38 That’s what we want it to do, but how are we sure that it works? Don’t get me wrong, you’re not trying to test print(), you’re trying to test code that calls print() and make sure that the right thing is printed when your function is called.

00:50 For the sake of this lesson, consider the following very simple method. greet() prints out hello and the name that is being passed in. I’m going to show you different ways of dealing with this code and mocking print() in order to handle it.

01:04 The first technique I’m going to show you is something called dependency injection. In order to use dependency injection, you have to change how greet() works.

01:13 greet() now takes two parameters, not just the name, but also a reference. This reference defaults to the print() function, which means display(), hi name, would get passed to print().

01:27 You’re going to use this in order to be able to mock out the value of display.

01:32 Lines 5 and 6 declare our mock_print(). This is the fake version to use under test.

01:38 Let’s look at what happens when you call mock _print. Passing in Bob, you don’t get a return value and it doesn’t print anything.

01:46 Line 6 adds a new attribute to the function. Because functions are objects in Python, you can add attributes just as if it were a class. You can look at this attribute.

01:58 It was assigned the message that was passed in. Lines 9, 10, and 11 are our actual test function. If I call the test, you’ll notice that it actually fails.

02:10 It fails because line 10 passed in with mock print is going to call greet(). greet() will say Hi name. The expected result is Hello name instead.

02:22 So our test has failed. Now I’m going to edit line 2 to change the string to say hello, which is what it was supposed to do in the first place. Rerun the test and the test passes.

02:35 No bells and whistles go off, but because the assert passed, you don’t get the failure traceback and the test is passed. If you were using a test harness like pytest, it would actually print out results showing you how many of your tests had passed.

02:51 One of the challenges with dependency injection is your code must be explicitly designed to use it. If you’re trying to test existing code that doesn’t have the correct hooks, a lot of work is necessary in order to properly test.

03:05 An alternative of this is to use the patch decorator inside of the mock library that comes with Python. Lines 1 and 2 are the original greet() method without the dependency injection code.

03:18 Line 5 imports patch. Line 7 is a decorator wrapping builtins.print. What this decorator does is, for the duration of the function it’s associated with, this test function, it’s going to replace the built-in print() with a mock.

03:35 That mock is passed in as the first argument to your test.

03:41 This means when greet() is called, print() is hit, mock replaces by monkey patching print() with the mock print and mock print is called instead.

03:51 You can then use the assert_called_with() function on the mock_print object to assert that hello and John were passed as arguments to print().

04:01 If they were not, then it would fail. It raises an exception.

04:06 Line 12, I’ve done it again with Eric. Lines 16 through 18, you wouldn’t normally have inside of a test. The asserts don’t show up when you call them so I want to print some information out to show you how this works.

04:17 The reason I’m calling sys.stdout instead of print() is because I’ve already mocked print(). If I call print() to try to show you the output, it’ll call the mocked version and you won’t see anything. So let me call test.

04:31 I call test and lines 17 and 18 put the information on the console.

04:37 This first statement call with Hello, Eric is from line 17. This is mock_print.call_args. The call_args property shows the last arguments that mock_print was called with.

04:49 Because Hello, Eric was called second, the value down here says it was called with Hello, Eric.

04:56 mock_print.call_args_list shows the entire call history. This can be really useful if you’ve got multiple calls going on. You can look at the mocked object and validate multiple arguments in one place.

05:09 mock is a very, very powerful library. It allows you to do much deeper things than just this patch. And if you’re interested in doing good unit tests, I encourage you to look into this some more.

05:20 Hand in hand with testing is doing the debugging so you can create the tests in the first place. In the next lesson I’ll show you how to use print() to help you debug and when you should be using logging instead.

Become a Member to join the conversation.