Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing that I have connected to a particular signal in Django

I have a Django app called "publisher", it connects to various signals in my django project, and when it receives them it sends a message to a rabbitmq queue. What I want to do is to be able to test that my setup code is connecting to the correct signals.

My app structure looks like:

publisher
    - __init__.py
    - signals.py
    - tests.py

my __init__.py looks like:

import signals

and my signals.py:

def receiver_function(*args, **kwargs):
    #Does rabbitmq stuff

my_interesting_signal.connect(receiver_function)

I has thought about patching the receiver function, and checking that the mock was called when I sent the signal:

tests.py:

class SignalsTeste(TestCase):

    def_test_connection(self):

        with patch('publisher.signals.receiver_function') as receiver_mock:
            my_interesting_signal.application_created.send(None)
            self.assertEquals(receiver_mock.call_count, 1)

However because the signals module is imported, and therefore the signal connections are made before the tests are run, this approach doesn't work as the connections are made before the function is patched......

Can anyone suggest an alternative strategy?

like image 743
Ctrlspc Avatar asked Nov 09 '12 15:11

Ctrlspc


People also ask

How do you call a signal in Django?

There are two ways to send signals in Django. To send a signal, call either Signal. send() (all built-in signals use this) or Signal.

How do I test my Django site?

The preferred way to write tests in Django is using the unittest module built-in to the Python standard library. This is covered in detail in the Writing and running tests document. You can also use any other Python test framework; Django provides an API and tools for that kind of integration.

Where is Pre_save signal in Django?

pre_save. This is sent at the beginning of a model's save() method. Arguments sent with this signal: sender.

How do I run a TestCase in Django?

Open /catalog/tests/test_models.py.TestCase , as shown: from django. test import TestCase # Create your tests here. Often you will add a test class for each model/view/form you want to test, with individual methods for testing specific functionality.


2 Answers

I ran into the same mocking problem you describe. My solution is to reach into Django's signal registry and assert that my function was registered with the correct signal.

Here's my test:

def test_signal_registry(self):
    from foo.models import bar_func  # The function I want to register.
    from django.db.models import signals
    registered_functions = [r[1]() for r in signals.pre_delete.receivers]
    self.assertIn(bar_func, registered_functions)

A little explanation about that list comprehension:

"pre_delete" is the instance of django.dispatch.dispatcher.Signal that I cared about in this case. You would be using your own "my_interesting_signal" in your example. Signals have an internal property called "receivers" that's a list of two-tuples, where the second element is a weakref to the function you register (hence r[1]). Calling a weakref returns the referent.

I had to play around with weakrefs to figure that much out:

import weakref
def foo():
    pass
w = weakref.ref(foo)
w() == foo

Hope this helps.

like image 189
Frank T Avatar answered Sep 19 '22 11:09

Frank T


A way to test if the signal is connected is disconnecting it and checking the result of this action. Calling <some_signal>.disconnect(...) returns True if the signal was disconnected or False if not.

For example we want to test that the post_save signal is connected to our receiver_function.

modules.py

def receiver_function(*args, **kwargs):
    pass

signals.post_save.connect(receiver_function)

tests.py

class SignalsTest(TestCase):
    def test_connection(self):
        result = signals.post_save.disconnect(receiver_function)

        self.assertTrue(result)

The call to disconnect must use the same arguments that the connect call(sender, dispatch_uid)

It's is necessary to connect the signal again after the test, if not it will remain disconnected

like image 26
alej0 Avatar answered Sep 22 '22 11:09

alej0