Django unittests + mock in signal testing : AssertionError: expected call not found (although actual and expected values match)

Viewed 18

I am using Django 3.2

I am writing unittests to test some custom signals. Here is a (necessarily simplified) version of the code I am testing:

/path/to/project/myapp/models.py

from .signals import signal_comment
from .constants import EVENT_COMMENT_DELETE

class Commentable(models.Model):

    def delete_comment(actor, *args, **kwargs):
        # do some checking etc ...
        signal_comment.send(sender= self.__class__, instance=self, comment_instance=comment, event_type = EVENT_COMMENT_DELETE, actor=actor) 

    class Meta:
        abstract = True



class InterestingArtefact(Commentable):
    pass   

/path/to/project/myapp/signals.py

from django.dispatch import Signal
# ...
signal_comment = Signal()

/path/to/project/myapp/tests/models/test_comment_models.py

from unittest.mock import MagicMock


class CatchSignal:

    def __init__(self, signal):
        self.signal = signal
        self.handler = MagicMock()


    def __enter__(self):
        self.signal.connect(self.handler)
        return self


    def __exit__(self, exc_type, exc_value, tb):
        self.signal.disconnect(self.handler)


class TestCommentModels(TestCase):
# ...
    
    def test_delete_comment_signal_emits_correct_data(self):

        with CatchSignal(signal_comment) as catcher:

            the_signal = catcher.signal
            handler = catcher.handler

            # Create comment (not using InterestingArtefact.add_comment(), so signal not sent on creation)
            ct = ContentType.objects.get_for_model(self.artefact)
            oid = self.artefact.id

            comment = Comment.objects.create(content_type=ct, object_id=oid, body='Hello world!', actor=self.approved_user)

            # is_ok, comment = self.artefact.add_comment(self.approved_user, **self.cparms_plain_text)
            self.assertIsInstance(comment, Comment)
            self.artefact.delete_comment(self.approved_user, comment.id)

            handler.assert_called_once_with(
                signal = the_signal,
                sender = InterestingArtefact,
                instance = self.artefact,
                comment_instance = comment,
                event_type = EVENT_COMMENT_DELETE,
                actor = self.approved_user
            )

When I run the test for this particular test, it fails, and I get the rather cryptic/not helpful message on the console:

in test_delete_comment_signal_emits_correct_data
    handler.assert_called_once_with(
  File "/usr/lib/python3.8/unittest/mock.py", line 925, in assert_called_once_with
    return self.assert_called_with(*args, **kwargs)
  File "/usr/lib/python3.8/unittest/mock.py", line 913, in assert_called_with
    raise AssertionError(_error_message()) from cause
AssertionError: expected call not found.
Expected: mock(signal=<django.dispatch.dispatcher.Signal object at 0x7f76858b41c0>, sender=<class 'myapp.models.InterestingArtefact'>, instance=<InterestingArtefact: InterestingArtefact - test artefact>, comment_instance=<Comment: Hello world!>, event_type=729, actor=<User: joebloggs>)
Actual: mock(signal=<django.dispatch.dispatcher.Signal object at 0x7f76858b41c0>, sender=<class 'myapp.models.InterestingArtefact'>, instance=<InterestingArtefact: InterestingArtefact - test artefact>, comment_instance=<Comment: Hello world!>, event_type=729, actor=<User: joebloggs>)

As can be seen from the error message above, the actual value matches the expected value, and since the mock code is receiving a value (i.e. the actual), I don't understand the error message: expected call not found.

What is going on? - and how do I resolve this?

0 Answers
Related