Home > Software engineering >  How to check what was written to a mock_open() fake file?
How to check what was written to a mock_open() fake file?

Time:01-27

I have a productive function that read a file and in some situations also write (more than once) to another file (with kind of detailed error output).

I am able to mock the file reading and give specific file content in the unittests. But I do not know how to test for what was written to the the second file which is also mocked via mock_open().

One important point is that I am not interested in writing a real file to the filesystem when unittesting.

That is the productive code:

import pathlib

def my_prod_code(fp):
    with fp.open('r') as if:
        result = if.read()

    with fp.with_suffix('.error.out').open('w') as of:
        of.write(f'Read {result}.')
        of.write('FIN.')

    return result

That is the unittest

import unittest
from unittest import mock
import pathlib

class MyTest(unittest.TestCase):
    def test_foobar(self, mock_unlink):
        opener = mock.mock_open(read_data='foobar')

        with mock.patch('pathlib.Path.open', opener):
            result = my_prod_code(pathlib.Path('file.in'))
            self.assertEqual(result, 'foobar')
            
            # Want to check for the written content also

CodePudding user response:

There is no built-in way to do this, so you either have to add your own handling in the mock, or use some package that fakes the file system.

Adding your own handling would mean to implement your own write, e.g. something like this:

class MockWriter:
    """Collect all written data."""
    def __init__(self):
        self.contents = ''

    def write(self, data):
        self.contents  = data

class MyTest(unittest.TestCase):
    def test_foobar(self):
        opener = mock.mock_open(read_data='foobar')
        writer = MockWriter()
        # replace the write method in the mock with your own
        opener.return_value.write = writer.write
        with mock.patch('pathlib.Path.open', opener) as f:
            result = my_prod_code(pathlib.Path('file.in'))
            self.assertEqual(result, 'foobar')
            self.assertEqual(writer.contents, 'Read foobar.FIN.')

The other possibility is to use a fake file system like pyfakefs:

from pyfakefs.fake_filesystem_unittest import TestCase

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

    def test_foobar(self):
        self.fs.create_file('file.in', contents='foobar')
        result = my_prod_code(pathlib.Path('file.in'))
        self.assertEqual(result, 'foobar')
        path = pathlib.Path('file.error.out')
        self.assertEqual(path.read_text(), 'Read foobar.FIN.')

This way, you don't have to do the mocking yourself and can use the standard file system functions, with the downside that you need an extra package that generates some test overhead.

Disclaimer:
I'm a contributor to pyfakefs.

  •  Tags:  
  • Related