Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper way to test Django signals

I'm trying to test sent signal and it's providing_args. Signal triggered inside contact_question_create view just after form submission.

My TestCase is something like:

    def test_form_should_post_proper_data_via_signal(self):         form_data = {'name': 'Jan Nowak'}         signals.question_posted.send(sender='test', form_data=form_data)         @receiver(signals.question_posted, sender='test')         def question_posted_listener(sender, form_data):             self.name = form_data['name']         eq_(self.name, 'Jan Nowak') 

Is this the proper way to test this signal? Any better ideas?

like image 704
bx2 Avatar asked Sep 28 '10 21:09

bx2


People also ask

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.

How do you 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. send_robust() .

What should you test in Django?

As discussed above, we should test anything that is part of our design or that is defined by code that we have written, but not libraries/code that is already tested by Django or the Python development team. For example, consider the Author model below.

Should I use signals Django?

Only use signals to avoid introducing circular dependencies. If you have two apps, and one app wants to trigger behaviour in an app it already knows about, don't use signals. The app should just import the function it needs and call it directly.


2 Answers

Simplest way to do what you asked in 2015:

from unittest.mock import patch  @patch('full.path.to.signals.question_posted.send') def test_question_posted_signal_triggered(self, mock):     form = YourForm()     form.cleaned_data = {'name': 'Jan Nowak'}     form.save()      # Check that your signal was called.     self.assertTrue(mock.called)      # Check that your signal was called only once.     self.assertEqual(mock.call_count, 1)      # Do whatever else, like actually checking if your signal logic did well. 

And with that, you just tested that your signal was properly triggered.

like image 55
José L. Patiño Avatar answered Sep 22 '22 06:09

José L. Patiño


I have an alternative suggestion using the mock library, which is now part of the unittest.mock standard library in Python 3 (if you're using Python 2, you'll have to pip install mock).

try:     from unittest.mock import MagicMock except ImportError:     from mock import MagicMock  def test_form_should_post_proper_data_via_signal(self):     """     Assert signal is sent with proper arguments     """       # Create handler     handler = MagicMock()     signals.question_posted.connect(handler, sender='test')      # Post the form or do what it takes to send the signal     signals.question_posted.send(sender='test', form_data=form_data)      # Assert the signal was called only once with the args     handler.assert_called_once_with(signal=signals.question_posted, form_data=form_data, sender="test") 

The essential part of the suggestion is to mock a receiver, then test whether or not your signal is being sent to that receiver, and called only once. This is great, especially if you have custom signals, or you've written methods that send signals and you want to ensure in your unit tests that they are being sent.

like image 30
bbengfort Avatar answered Sep 22 '22 06:09

bbengfort