Search code examples
pythondjangopython-unittestdjango-testingdjango-signals

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


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?


Solution

  • 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