[TIP] My Mock Generator Fixture

Peter Kogan kogan.peter at gmail.com
Wed Jan 5 02:58:08 PST 2022


Hi folks,
I created a mock generator for Pytest and published it on PyPI
<https://pypi.org/project/pytest-mock-generator/>.
This fixture saves me time when mocking objects, so I'm sharing this with
you in the hope that it can save you some time as well. I'd appreciate any
feedback you may have.

The usage is pretty simple:

Assume you have a module named  tested_module.py which holds a function to
process a string sent to it and then add it to a zip file:

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).
Let's assume that you don't want to create a file on the hard drive and
wish to mock it instead.

Here is where the mock generator (mg for short) fixture comes to play - it
would generate both the arrange and assert sections for you:

from tests.sample.code.tested_module import process_and_zip
def test_process_and_zip(mocker, *mg*):
    # arrange: todo
    *mg.generate_uut_mocks_with_asserts(process_and_zip)*

    # act: invoking the tested code
    process_and_zip('/path/to.zip', 'in_zip.txt', 'foo bar')

    # assert: todo

Note the bold mg fixture usage.

When you execute the test, the following output is printed to the console
and copied to the clipboard:

# mocked dependenciesmock_ZipFile =
mocker.MagicMock(name='ZipFile')mocker.patch('tests.sample.code.tested_module.zipfile.ZipFile',
new=mock_ZipFile)# calls to generate_asserts, put this after the
'act'mg.generate_asserts(mock_ZipFile, name='mock_ZipFile')

You can copy the generated code into the appropriate section and get the
following test:

from tests.sample.code.tested_module import process_and_zip
def test_process_and_zip(mocker, *mg*):
    # 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: todo
    # calls to generate_asserts, put this after the 'act'*
mg.generate_asserts(mock_ZipFile, name='mock_ZipFile')*

So the arrange part is ready and we're left with filling the assets. In
order to generate the asserts code, run the test function once more and
you'll get this input:

assert 1 == mock_ZipFile.call_countmock_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)

The generated asserts are the actual recorded calls to the mock, made
inside process_and_zip. They cover the creation of the zip file and
context, and the call to create a file inside the zip and fill it with data.

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)

Pretty easy and quick, right? Imagine all the time you can save in wiring
mocks and writing the asserts.


*If you've made it till the end of the email, please let me know what you
think :)*

*And if you are using the library and encounter any issue, you can open an
issue here
<https://github.com/pksol/pytest-mock-generator/issues/new/choose>.*


Thanks.
Peter
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.idyll.org/pipermail/testing-in-python/attachments/20220105/5145934c/attachment-0001.html>


More information about the testing-in-python mailing list