Home > other >  Python recursively mock entire module for unittesting
Python recursively mock entire module for unittesting

Time:12-12

I'm trying to unit test a class, let's call it A. As part of A's instantiation, it tries to create instances of many classes from a package external.

from external import B, C, D

class A
    def __init__(self, some_config):
        b_config, c_config, d_config = self._generate_config(some_config)
        b = B(b_config)
        c = C(c_config)
        d = D(d_config)

I don't want to instantiate anything from external for my unit test, but the nature of the codebase is that I also don't want to pass b, c, or d in as external arguments for the constructor.

I know using mock.patch, I can write a test

@mock.patch(external.D, autospec=True)
@mock.patch(external.C, autospec=True)
@mock.patch(external.B, autospec=True)
def test(self, b_patch: mock.MagicMock,
               c_patch: mock.MagicMock,
               d_patch: mock.MagicMock):
    b_patch.return_value = mock.Mock()
    c_patch.return_value = mock.Mock()
    d_patch.return_value = mock.Mock()

    test_a = A()
    # Assert no problems with instantiating A
    # AKA self._generate_config worked

But is there an easy way to mark all classes inside external such that they should be mocked like above?

CodePudding user response:

Your sample code isn't self-consistent -- in your first example you're doing from external import B and in the second example it looks like you did import external. In the second form you're able to just patch external:

import external

class A:
    def __init__(self):
        b = external.B()
        c = external.C()
        d = external.D()

from unittest.mock import patch

@patch('__main__.external')
def test(mock_external):
    test_a = A()

test()

The above test passes regardless of the contents of external.py (as long as it doesn't raise an error on the initial import) because the test patches out the entire module.

It's not necessary to individually assign all the different attributes of external to Mock objects because every attribute of a Mock is already an auto-created Mock unless you set it to something else.

If there is no external name in the module under test (because you did from external import B etc) you need to individually patch the B, C, and D names.

  • Related