[TIP] My Mock Generator Fixture

Chris Wesseling chris.wesseling at cwi.nl
Wed Jan 5 06:20:23 PST 2022


On 2022-01-05T12:58:08+0200, Peter Kogan wrote:
[...]
> import zipfile
> def process_and_zip(zip_path, file_name, contents):
>     processed_contents = "processed " + contents  # some complex logic
>     with zipfile.ZipFile(zip_path, 'w') as zip_container:
>         zip_container.writestr(file_name, processed_contents)
> 
> This is the function that you want to test (aka Unit Under Test or UUT).
[... generating a test ...]
> Now you can place all or part of the assert calls into your function and
> you're done!
> 
> from tests.sample.code.tested_module import process_and_zip
> def test_process_and_zip(mocker):
>     # arrange:
>     # mocked dependencies
>     mock_ZipFile = mocker.MagicMock(name='ZipFile')
>     mocker.patch('tests.sample.code.tested_module.zipfile.ZipFile',
> new=mock_ZipFile)
> 
>     # act: invoking the tested code
>     process_and_zip('/path/to.zip', 'in_zip.txt', 'foo bar')
> 
>     # assert:
>     assert 1 == mock_ZipFile.call_count
>     mock_ZipFile.assert_called_once_with('/path/to.zip', 'w')
>     mock_ZipFile.return_value.__enter__.assert_called_once_with()
>     mock_ZipFile.return_value.__enter__.return_value.writestr.assert_called_once_with('in_zip.txt',
> 'processed foo bar')
>     mock_ZipFile.return_value.__exit__.assert_called_once_with(None, None, None)

Very neat, but what does this test? Doesn't this tie down the exact
implementation to the letter? The only real assertion I spot here is
whether `# some complex logic` is doing its job right. If its that
complex and needs testing, I'd pull it out to a function `_process` of
contents and test it directly, without any need for mocks.

And if I deem the writing of the actual zip non-trivial, I'd write an
integration test with the `tmp_path` fixture that tests the resulting
file.

I've had problems refactoring codebases that were completely tied down
with mocks. Automating their creation would only tempt me down that path
again. I'd rather think about invariants of `# some complex logic` and
use something like hypothesis[1] to generate all kinds of strings for
me to use as `contents`.

That said, it's still very neat. And I imagine I could learn a thing or
two if I were to inspect your implementation of this `mg` fixture. So I
suspect this was just


[1]: https://hypothesis.readthedocs.io

-- 
Chris Wesseling
Centrum Wiskunde & Informatica (CWI)
https://www.cwi.nl/people/chris-wesseling



More information about the testing-in-python mailing list