1

So I have function in my Django app business logic I want to test:

def parse_sectors_from_csv():
    sectors_csv = open(os.path.join(settings.BASE_DIR, 'sector', 'fixtures', 'sectors.csv'))
    sectors_csv_reader = csv.DictReader(sectors_csv)
    return [
        {
            'model': 'sector.sector',
            'id': index,
            'fields': {
                'name': row.get('Sector'),
                'slug': slugify(row['Sector']),
                'type_id': row.get('Type_Id')
            }
        }
        for index, row in enumerate(sectors_csv_reader, 1)
    ]

I already test it for file existence and for existence of heading row.

Now I want to mock

open(os.path.join(settings.BASE_DIR, 'sector', 'fixtures', 'sectors.csv'))

In my test.py I wrote

with patch('__builtin__.open', mock_open(read_data=sectors_csv_mock), create=True) as m:
        sectors = service.parse_sectors_from_csv()
        print('Sectors:', sectors)
        self.assertEqual(expected_sectors, sectors)

But as I understand, it's passes empty file to function as it prints Sectors: []

I read mock_open dock for a few times and still can't figure it out.

>>> with patch('__main__.open', mock_open(read_data='bibble')) as m:
...     with open('foo') as h: # what's happening here?
...         result = h.read() # here?
...
>>> m.assert_called_once_with('foo') # and here?
>>> assert result == 'bibble'
Leonid Ivanov
  • 331
  • 5
  • 15

2 Answers2

6

First rule of mocking: don't mock the objects/methods where they are defined, mock them where they are used. So no __builtin__.open but my_app.my_file.open

Second, I don't know what mock_open is but you can do it the normal mock way:

with mock.patch('my_app.my_file.open') as mocked_open:
    mocked_open.return_value = StringIO('foo')

This works no matter what.

P.S.

Always use with when handling files

Seif
  • 1,058
  • 11
  • 19
0

Whenever I feel the need to mock, I first ask myself whether it's feasible to refactor the code along these lines:

  1. Stuff that is algorithmically non-trivial and that I might want to unit test with actual assertions.

  2. Stuff so simple that I either (a) hardly need to test it or (b) could sufficiently test with a single end-to-end test that merely exercises the code without making any assertions.

For example:

####
# Hardly worth testing.
####

def parse_sectors_from_csv():
    file_path = sectors_file_path()
    with open(file_path) as fh:
        return do_parse_sectors_from_csv(fh)

####
# Easily tested without mocks.
####

def do_parse_sectors_from_csv(fh):
    reader = csv.DictReader(fh)
    return do_parse_sectors_from_csv(reader)

def sectors_file_path():
    return os.path.join(settings.BASE_DIR, 'sector', 'fixtures', 'sectors.csv')

def parse_sectors_from_csv(rows):
    return [
        {
            'model': 'sector.sector',
            'id': index,
            'fields': {
                'name': row.get('Sector'),
                'slug': slugify(row['Sector']),
                'type_id': row.get('Type_Id')
            }
        }
        for index, row in enumerate(rows, 1)
    ]
FMc
  • 41,963
  • 13
  • 79
  • 132