How to do unit testing of functions writing files using Python's 'unittest'

Viewed 85244

I have a Python function that writes an output file to disk.

I want to write a unit test for it using Python's unittest module.

How should I assert equality of files? I would like to get an error if the file content differs from the expected one + list of differences. As in the output of the Unix diff command.

Is there an official or recommended way of doing that?

7 Answers

I always try to avoid writing files to disk, even if it's a temporary folder dedicated to my tests: not actually touching the disk makes your tests much faster, especially if you interact with files a lot in your code.

Suppose you have this "amazing" piece of software in a file called main.py:

"""
main.py
"""

def write_to_file(text):
    with open("output.txt", "w") as h:
        h.write(text)

if __name__ == "__main__":
    write_to_file("Every great dream begins with a dreamer.")

To test the write_to_file method, you can write something like this in a file in the same folder called test_main.py:

"""
test_main.py
"""
from unittest.mock import patch, mock_open

import main


def test_do_stuff_with_file():
    open_mock = mock_open()
    with patch("main.open", open_mock, create=True):
        main.write_to_file("test-data")

    open_mock.assert_called_with("output.txt", "w")
    open_mock.return_value.write.assert_called_once_with("test-data")

If you can use it, I'd strongly recommend using the following library: http://pyfakefs.org

pyfakefs creates an in-memory fake filesystem, and patches all filesystem accesses with accesses to the fake filesystem. This means you can write your code as you normally would, and in your unit tests, just make sure to initialize the fake filesystem in your setup.

from pyfakefs.fake_filesystem_unittest import TestCase

class ExampleTestCase(TestCase):
    def setUp(self):
        self.setUpPyfakefs()

    def test_create_file(self):
        file_path = '/test/file.txt'
        self.assertFalse(os.path.exists(file_path))
        self.fs.create_file(file_path)
        self.assertTrue(os.path.exists(file_path))

There is also a pytest plugin if you prefer to run your tests with pytest.

There are some caveats to this approach - you can't use it if you're accessing the filesystem via C libraries, because pyfakefs can't use patch those calls. However, if you're working in pure Python, I've found it to be a tremendously useful library.

Related