2010-09-28 16 views
23

Sto provando a testare il segnale inviato e sta fornendo_args. Segnale attivato all'interno della vista contact_question_create subito dopo l'invio del modulo.Modo corretto per testare i segnali di Django

mio TestCase è qualcosa di simile:

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') 

E 'questo il modo corretto per testare questo segnale? Qualche idea migliore?

+0

correlati: http://stackoverflow.com/questions/13112302/how-do-i-mock-a-django-signal-handler –

risposta

3

Ho risolto il problema da solo. Penso che la soluzione migliore sia la seguente:

def test_form_should_post_proper_data_via_signal(self): 
     # define the local listener 
     def question_posted_listener(sender, form_data, **kwargs): 
      self.name = form_data['name'] 

     # prepare fake data 
     form_data = {'name': 'Jan Nowak'} 

     # connect & send the signal 
     signals.question_posted.connect(question_posted_listener, sender='test') 
     signals.question_posted.send(sender='test', form_data=form_data) 

     # check results 
     eq_(self.name, 'Jan Nowak') 
+4

Ciò non fallirà se un segnale viene emesso due volte o più. – jpic

1

Perché testare la struttura? Django ha già dei test unitari per il dispatcher di segnali. Se non credi che il tuo quadro sia a posto basta allegare i test unitari al tuo corridore di prova.

+3

Si prega di notare che non sta testando come la logica dei segnali di Django agisce, ma in realtà verifica quando e come * il suo codice * innesca un segnale Django. –

4

Lo scopo di questo non è quello di testare il meccanismo di segnalazione sottostante, ma piuttosto è un test unitario importante per garantire che qualsiasi segnale emesso dal proprio metodo sia effettivamente emesso con gli argomenti appropriati. In questo caso, sembra un po 'banale dal momento che è un segnale interno di Django, ma immagina se hai scritto il metodo che stava emettendo un segnale personalizzato.

6

è necessario:

  • affermare un segnale è stato emited con argomenti adeguati e,
  • un determinato numero di volte e,
  • in modo appropriato.

È possibile utilizzare l'app mock_django che fornisce uno mock for signals.

Esempio:

from mock import call 


def test_install_dependency(self): 
    with mock_signal_receiver(post_app_install) as install_receiver: 
     self.env.install(self.music_app) 
     self.assertEqual(install_receiver.call_args_list, [ 
      call(signal=post_app_install, sender=self.env, 
       app=self.ukulele_app), 
      call(signal=post_app_install, sender=self.env, 
       app=self.music_app), 
     ]) 
+0

Nota che la versione 1.6.5 di mock_django non funziona con Django 1.5 o successivo. (Al momento della scrittura, 1.6.5 è la versione con tag più aggiornata e quella che è in PyPI.) –

+1

@jpic potresti aggiungere una riga "dalla chiamata di importazione fittizia" perché non è ovvio? – syabro

+1

Ho pubblicato un aggiornamento usando la libreria di simulazioni invece di mock_django. Vedi sotto. – bbengfort

11

Ho un suggerimento alternativo utilizzando la libreria mock, che ora fa parte della libreria standard unittest.mock in Python 3 (se si sta utilizzando Python 2, dovrete 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) 

La parte essenziale del suggerimento è quello di prendere in giro un ricevitore, quindi verificare se il vostro segnale viene trasmesso a quella ricevente, e chiamato solo una volta. Questo è ottimo, specialmente se si dispone di segnali personalizzati o se si sono scritti metodi che inviano segnali e si desidera assicurare nei test dell'unità che vengano inviati.

+0

AttributeError: l'oggetto 'function' non ha attributo 'connect' – chefarov

20

più semplice modo per fare quello che hai chiesto nel 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. 

E con questo, basta testato che il segnale è stato attivato correttamente.

+2

Ciao José, non ho potuto farlo funzionare - penso che potrebbe essere stato qualcosa a che fare con il dispatch_uid. Ho finito per usare l'approccio 'with patch (' xyz ') come mocked_handler'. – stephendwolff

+2

Penso che sarebbe più preciso affermare non solo chiamare il fatto, ma anche chiamare argomenti, usando mock.assert_called_once_with –

Problemi correlati