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?
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.
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.
pre_save. This is sent at the beginning of a model's save() method. Arguments sent with this signal: sender.
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.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With