Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to retrieve all the content of calls made to a mock?

I'm writing a unit test for a function that takes an array of dictionaries and ends up saving it in a CSV. I'm trying to mock it with pytest as usual:

csv_output = (
    "Name\tSurname\r\n"
    "Eve\tFirst\r\n"
)
with patch("builtins.open", mock_open()) as m:
    export_csv_func(array_of_dicts)

assert m.assert_called_once_with('myfile.csv', 'wb') is None
[and here I want to gather all output sent to the mock "m" and assert it against "csv_output"]

I cannot get in any simple way all the data sent to the mock during the open() phase by csv to do the comparison in bulk, instead of line by line. To simplify things, I verified that the following code mimics the operations that export_csv_func() does to the mock:

with patch("builtins.open", mock_open()) as m:
  with open("myfile.csv", "wb") as f:
    f.write("Name\tSurname\r\n")
    f.write("Eve\tFirst\r\n")

When I dig into the mock, I see:

>>> m
<MagicMock name='open' spec='builtin_function_or_method' id='4380173840'>
>>> m.mock_calls
[call('myfile.csv', 'wb'),
 call().__enter__(),
 call().write('Name\tSurname\r\n'),
 call().write('Eve\tFirst\r\n'),
 call().__exit__(None, None, None)]
>>> m().write.mock_calls
[call('Name\tSurname\r\n'), call('Eve\tFirst\r\n')]
>>> dir(m().write.mock_calls[0])
['__add__'...(many methods), '_mock_from_kall', '_mock_name', '_mock_parent', 'call_list', 'count', 'index']

I don't see anything in the MagickMock interface where I can gather all the input that the mock has received.

I also tried calling m().write.call_args but it only returns the last call (the last element of the mock_calls attribute, i.e. call('Eve\tFirst\r\n')).

Is there any way of doing what I want?

like image 212
Ender Avatar asked Dec 18 '19 10:12

Ender


1 Answers

You can create your own mock.call objects and compare them with what you have in the .call_args_list.

from unittest.mock import patch, mock_open, call

with patch("builtins.open", mock_open()) as m:
    with open("myfile.csv", "wb") as f:
        f.write("Name\tSurname\r\n")
        f.write("Eve\tFirst\r\n")

# Create your array of expected strings
expected_strings = ["Name\tSurname\r\n", "Eve\tFirst\r\n"]
write_calls = m().write.call_args_list
for expected_str in expected_strings:
    # assert that a mock.call(expected_str) exists in the write calls
    assert call(expected_str) in write_calls

Note that you can use the assert call of your choice. If you're in a unittest.TestCase subclass, prefer to use self.assertIn.

Additionally, if you just want the arg values you can unpack a mock.call object as tuples. Index 0 is the *args. For example:

for write_call in write_calls:
    print('args: {}'.format(write_call[0]))
    print('kwargs: {}'.format(write_call[1]))
like image 129
wholevinski Avatar answered Oct 29 '22 04:10

wholevinski