Unit Testing with Python (Part 4) – File Operations

Previously (here, here and here) we looked at users.py and how we can create a set of tests using mocks to validate it’s working as intended. The subject we were mocking in those previous examples was file_utils.py. Here’s what file_utils.py looks like;

def write_file(path, content):
file_to_write = open(path, "w")
def read_file(path):
with open(path, 'r') as file_to_read:
data = file_to_read.read()
return data

view raw
hosted with ❤ by GitHub

Very simple, one function writes to a file and the other one reads from it. Easy!  Let’s write some unit tests for it!

Remember when it comes to testing we want to avoid;

  1. Testing beyond our function. The test for file_utils.py isn’t the right place to validate Python’s open.
  2. Creating actual artifcats, in this case files to read from or write to. That will make the tests slower and is just unnecessary.

So to help us, we can again use mock, it even comes with a specific function to help us in this case, mock_open. That link explains the benefits of using mock_open rather than trying to create your own and is definetly worth reading before continuining.

Here’s some tests for the file_utils.py;

import unittest
import mock
import file_utils
class FileUtilsTest(unittest.TestCase):
def test_read_file(self):
with mock.patch("__builtin__.open", mock.mock_open(read_data="MOCKED"), create=True) as mock_file:
result = file_utils.read_file("path")
mock_file.assert_called_once_with("path", "r")
assert result == "MOCKED"
def test_write_file(self):
with mock.patch("__builtin__.open", mock.mock_open(), create=True) as mock_file:
file_utils.write_file("path", "content")
mock_file.assert_called_once_with("path", "w")
if __name__ == '__main__':

view raw
hosted with ❤ by GitHub

Given the previous posts this should look pretty familiar, but let’s look at some of the subtle differences.

  with mock.patch("__builtin__.open", mock.mock_open(read_data="MOCKED"), create=True) as mock_file:

To test the read, we mock the builtin open, and replace it with our mock_open version. The read_data or the output in this case we’ve change to be “MOCKED”, that’s how we can control the contents of our mocked file.

The write is a bit more involved. First we create our mock in a similar fashion;

 with mock.patch("__builtin__.open", mock.mock_open(), create=True) as mock_file:

Then we specify a return_value for our mock, and assert that the mocked write function is called at least once.


The reason being that we didn’t bother specifying a mock object up front, so we got the default which doesn’t have a mock for write.

That’s pretty much it! Thanks again for reading, as you can see mock once again provides useful features to help us easily control how our tests run. In the next posts we’ll be looking at how we can have our mocks throw exceptions, and then consider the implications of writing good unit tests.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s