fose
fose

Reputation: 739

Python 3.6 Beginner: Why getting a blank AssertionError for assert_called_once_with (unit test & mocking)

Hi I'm a developer who is new to Python and I'm trying to get the following mocked unit test running. Any help would be appreciated, thank you in advance!

I have a class class LeadsConsumer (of module leads_consumer) of which I want to unit test the method _process_lead.

from .helper import dynamo_helpers

class LeadsConsumer:

    def __init__(self):
        self._dynamodb = dynamo_helpers.Dynamo(AWS_RUNTIME_REGION, ENVIRONMENT, VERTICAL)

    def _process_lead(self, _lead):
        role_set = self._dynamodb.get_role_set(_lead.getPartner_id())
        if role_set is None:
            role_set = self._dynamodb.create_partner(_lead)
        if role_set['is-partner'] is True:
            return

        user = self._dynamodb.get_user(_lead.getEmail())
        if user is None:
            user = self._dynamodb.create_user(_lead)
        else:
            user = self._dynamodb.update_user(_lead)

And the unit test looks like:

class LeadsConsumerTest(TestCase):

    @mock.patch('src.leads_consumer.dynamo_helpers.Dynamo')
    def test_process_lead_new_lead(self, mock_dynamo_class):
        # given
        mock_dynamo_instance = mock_dynamo_class.return_value
        mock_dynamo_instance.get_role_set.return_value = None
        mock_dynamo_instance.create_partner.return_value = self._given_role_set(False)
        mock_dynamo_instance.get_user.return_value = None
        mock_dynamo_instance.create_user.return_value = None

        _leads_consumer = LeadsConsumer()
        _lead = self._given_lead(False)

        # when
        _leads_consumer._process_lead(_lead)

        # then
        assert mock_dynamo_instance.get_role_set.assert_called_once_with(_lead.getPartner_id())
        assert mock_dynamo_instance.create_partner.assert_called_once()
        assert mock_dynamo_instance.get_user.assert_called_once()
        assert mock_dynamo_instance.create_user.assert_called_once_with(_lead)

When I run this test I get the following AssertionError:

in test_process_lead_new_lead
assert mock_dynamo_instance.get_role_set.assert_called_once_with(_lead.getPartner_id())
AssertionError

The blank AssertionError (without any explanation of what is wrong) is kind of confusing me. If I change

assert mock_dynamo_instance.get_role_set.assert_called_once_with(_lead.getPartner_id())

to

assert mock_dynamo_instance.get_role_set.assert_called_once_with("foobar", _lead.getPartner_id())

I get

AssertionError: Expected call: get_role_set('foobar', '1000000')
Actual call: get_role_set('1000000')

This is, as I understand it, kind of to be expected. But then why do I get an AssertionError if I leave the test as in the original above? I don't understand this. The modified test shows the method get_role_set has been called with ('foobar', '1000000'). So if I omit the 'foobar', why won't the test run through without an error? (I actually get the same error for the last line of the test if I comment out the other asserts.)

Can anyone tell me what I'm doing wrong here? Also hints on other flaws in the rookie code would be highly appreciated. Thanks.

Upvotes: 0

Views: 220

Answers (1)

Daniel Roseman
Daniel Roseman

Reputation: 600026

You're confusing two syntaxes here. assert is a command which you use with normal expressions; it raises an exception if the expression is not truthy.

assert_called_once_with is a method provided by the Mock object, which again raises an AssertionError if it fails, but returns None otherwise.

So you have two assertions going on here. The result from the assert_called_once_with call is None, which is of course falsy; so you get an AssertionError from the assert statement.

The solution is to simply remove all the assert keywords and just leave the assert_called_once and assert_called_once_with method calls.

Upvotes: 1

Related Questions