2012-11-09 24 views
6

Mam aplikację Django o nazwie "publisher", łączy się z różnymi sygnałami w moim projekcie django, a kiedy je otrzyma, wysyła wiadomość do kolejki rabbitmq. Chciałbym móc przetestować, czy mój kod instalacyjny łączy się z prawidłowymi sygnałami.Testowanie, że podłączyłem się do konkretnego sygnału w Django

Moja aplikacja struktura wygląda następująco:

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

mój __init__.py wygląda następująco:

import signals 

i mojego signals.py:

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

my_interesting_signal.connect(receiver_function) 

ja nie pomyślał o łatanie funkcja odbiornika i sprawdzenie, czy próbka została wywołana, gdy wysłałem sygnał:

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) 

Jednak ponieważ moduł sygnały są importowane, a więc połączenia sygnałowe są wykonane przed prowadzone są testy, takie podejście nie działa jak połączenia są wykonane przed funkcja jest załatany ......

Czy ktoś może zaproponować alternatywną strategię?

+0

Nie wiem, czy mam to proste, ale śledzę moje sygnały zapisu do dziennika w funkcji odbiornika def function_function (* args, ** kwargs): – PepperoniPizza

Odpowiedz

12

Wpadłem na ten sam kpiący problem, który opisałeś. Moim rozwiązaniem jest sięgnięcie do rejestru sygnałów Django i stwierdzenie, że moja funkcja została zarejestrowana z prawidłowym sygnałem.

Oto moja próba:

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) 

Trochę wyjaśnienie o tym listowego:

„pre_delete” jest instancją django.dispatch.dispatcher.Signal że mi zależało w tej sprawie. W twoim przykładzie używałbyś własnego "my_interesting_signal". Sygnały mają wewnętrzną właściwość zwaną "odbiornikami", która jest listą dwóch krotek, gdzie drugi element jest słabym ogniwem funkcji, którą rejestrujesz (stąd r [1]). Wywołanie weakref zwraca referencję.

musiałem bawić z weakrefs, aby dowiedzieć się, że wiele:

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

nadzieję, że to pomaga.

+0

Nie sądzę, to jest całkiem w porządku. Jeśli bar_func znajduje się w 'models.py', to tak, to zostanie automatycznie zaimportowane przez Django, a więc połączone z sygnałem. Ale jeśli jest w 'signals.py', to niekoniecznie. Również akt importu twojego odbiorcy w teście automatycznie go połączy, więc test przejdzie, nawet jeśli nie łączysz go gdzie indziej. – seddonym

1

Sposób sprawdzenia, czy podłączony jest sygnał, powoduje odłączenie go i sprawdzenie wyniku tej czynności. Wywołanie <some_signal>.disconnect(...) zwraca True, jeśli sygnał został rozłączony lub False, jeśli nie. Na przykład chcemy przetestować, czy sygnał post_save jest podłączony do naszego receiver_function.

modules.py

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

signals.post_save.connect(receiver_function) 

testów.py

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

     self.assertTrue(result) 

Wezwanie do disconnect musi używać tych samych argumentów, które jest connect call (sender, dispatch_uid)

Jest to konieczne, aby ponownie podłączyć sygnał po teście, jeśli nie będzie on pozostać odłączony

0

Jest to dość podstępne, ponieważ jak mówisz, jeśli wyśmiewasz lub importujesz wszystko z pliku, do którego dołączasz odbiornik, automatycznie go łączysz. Jest to cały Twój zestaw testów , a nie tylko w danym pliku testowym. Oto fragment, którego możesz użyć, ale musisz zdyscyplinować śledzenie komentarzy dotyczących unikania importu w pliku odbiorników.

from django.test import TestCase 

class ReceiverConnectionTestCase(TestCase): 
    """TestCase that allows asserting that a given receiver is connected 
    to a signal. 

    Important: this will work correctly providing you: 
     1. Do not import or patch anything in the module containing the receiver 
      in any django.test.TestCase. 
     2. Do not import (except in the context of a method) the module 
      containing the receiver in any test module. 

    This is because as soon as you import/patch, the receiver will be connected 
    by your test and will be connected for the entire test suite run. 

    If you want to test the behaviour of the receiver, you may do this 
    providing it is a unittest.TestCase, and there is no import from the 
    receiver module in that test module. 

    Usage: 

     # myapp/receivers.py 
     from django.dispatch import receiver 
     from apples.signals import apple_eaten 
     from apples.models import Apple 

     @receiver(apple_eaten, sender=Apple) 
     def my_receiver(sender, **kwargs): 
      pass 


     # tests/integration_tests.py 
     from apples.signals import apple_eaten 
     from apples.models import Apple 

     class TestMyReceiverConnection(ReceiverConnectionTestCase): 
      def test_connection(self): 
       self.assert_receiver_is_connected(
        'myapp.receivers.my_receiver', 
        signal=apple_eaten, sender=Apple) 

    """ 
    def assert_receiver_is_connected(self, receiver_string, signal, sender): 
     receivers = signal._live_receivers(sender) 
     receiver_strings = [ 
      "{}.{}".format(r.__module__, r.__name__) for r in receivers] 
     if receiver_string not in receiver_strings: 
      raise AssertionError(
       '{} is not connected to signal.'.format(receiver_string)) 

To działa, ponieważ Django działa django.test.TestCase przed unittest.TestCase.

Powiązane problemy