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?