Django testing custom signal arguments with assertIsInstance: AssertionError: <class 'path.to.Foo'> is not an instance of <class 'path.to.Foo'>

Viewed 260

I am using Django 3.2

I have written an app that raises custom events, and I am writing tests for the apps.

Here is the relevant section of my code:

class TestAppModel(TestCase):
    # .. set up instance variables etc.

    def test_liked_signal_emits_correct_data(self):
    
        self.signal_was_called = False
        self.sender = None
        self.instance = None   
        self.event_type = None
        self.actor = None

        def handler(sender, instance, event_type, actor, **kwargs):
            self.signal_was_called = True
            self.sender = sender
            self.instance = instance   
            self.event_type = event_type
            self.actor = actor         
            
        item_liked.connect(handler)

        self.daddy.like(self.admin_user)

 
        # Check that the sender is a Foo
        self.assertIsInstance(self.sender, Foo) # <- Nonsensical error emitted here

When I run the test, I get the error message:

AssertionError: <class 'social.models.Foo'> is not an instance of <class 'social.models.Foo'>

Which obviously, is a nonsensical error message that doesn't help me solve the problem.

My question is why am I not able to check the instance type using assertIsInstance, and how do I check the class type in the signal receiver?

1 Answers

The sender in a signal is typically the class of the instance. Hence you writing self.assertIsInstance(self.sender, Foo) is equivalent to writing self.assertIsInstance(Foo, Foo) which of course is not correct as Foo is not an instance of Foo (It likely is an instance of object, type and ModelBase (metaclass of Model)). For instance see the following snippet:

class Foo:
    pass

print(isinstance(Foo, Foo)) # False
print(isinstance(Foo, object)) # True
print(isinstance(Foo, type)) # True

Instead you want to use assertIs instead of assertIsInstance:

class TestAppModel(TestCase):
    # .. set up instance variables etc.

    def test_liked_signal_emits_correct_data(self):
    
        self.signal_was_called = False
        self.sender = None
        self.instance = None   
        self.event_type = None
        self.actor = None

        def handler(sender, instance, event_type, actor, **kwargs):
            self.signal_was_called = True
            self.sender = sender
            self.instance = instance   
            self.event_type = event_type
            self.actor = actor         
            
        item_liked.connect(handler)

        self.daddy.like(self.admin_user)

 
        # Check that the sender is a Foo
        self.assertIs(self.sender, Foo) # The sender should be Foo itself
        self.assertIsInstance(self.instance, Foo) # The instance should be an instance of Foo
Related