Pittfall
Pittfall

Reputation: 2851

How can I mock a function called from a dictionary?

test.py:

@pytest.mark.django_db
def test_b_called(mocker):
    b = mocker.patch('app.service.b')

    service.a('b')
    assert b.called

service.py:

def a(type):
    _actions[type]()

def b():
    pass

_actions = { 'b': b }

My test will fail as my patch does not work as I expected. What am I doing wrong here? This definitely works if a calls b directly and not using that dictionary. I have tested for this. I know you can mock a dictionary with patch.dict but then how would I test that b was called?

Upvotes: 3

Views: 2332

Answers (2)

Pittfall
Pittfall

Reputation: 2851

I was trying to mock the dictionary action with a function that does nothing. Instead, I should have mocked it to a MagicMock function which is what a patch does as well.

patch.dict(
    'app.service._actions',
    {'b': MagicMock} # MagicMock is imported from unittest.mock
)

Upvotes: 0

l33tHax0r
l33tHax0r

Reputation: 1721

So I would argue that these are two separate unit tests, one for function a and one for the dictionary _actions.

_actions is not just a simple dictionary but in a sense a dynamic function call. So if you are truly testing just function a then you need to patch the _actions object and just test the functionality in the scope of the function.

_actions is out of the testing scope and should be tested individually like any other method.

from unittest import TestCase
from unittest.mock import patch
from stack_overflow import a,b,c, _actions

class TestStack(TestCase):

    def setUp(self):
        super().setUp()

    def tearDown(self):
        super().tearDown()

    @patch.dict('stack_overflow._actions',{'b':b})
    def test_a(self):
        self.assertEqual(5,a('b'))

    def test__actions_def_b(self):
        self.assertEqual(_actions['b'],b)

    def test__actions_def_c(self):
        self.assertEqual(_actions['c'],c)
def a(type):
    current_actions = _actions
    return _actions[type]()

def b():
    return 5

def c():
    return 7

_actions = { 'b': b, 'c': c}

Upvotes: 1

Related Questions