2015-03-28 10 views
7

potrzebne trzy metody patch (_send_reply, _reset_watchdog i _handle_set_watchdog) z metod próbnych przed badaniem połączenia z czwartym sposobem (_handle_command) w teście jednostkowej kopalni.korzystny sposób łatania wiele metod Pythona badanej jednostki

Od patrząc na dokumentacji pakietu mock, istnieje kilka sposobów, mogę iść o nim:

Z patch.multiple jako dekorator

@patch.multiple(MBG120Simulator, 
       _send_reply=DEFAULT, 
       _reset_watchdog=DEFAULT, 
       _handle_set_watchdog=DEFAULT, 
       autospec=True) 
def test_handle_command_too_short_v1(self, 
            _send_reply, 
            _reset_watchdog, 
            _handle_set_watchdog): 
    simulator = MBG120Simulator() 
    simulator._handle_command('XA99') 
    _send_reply.assert_called_once_with(simulator, 'X?') 
    self.assertFalse(_reset_watchdog.called) 
    self.assertFalse(_handle_set_watchdog.called) 
    simulator.stop() 

Z patch.multiple jako kierownik kontekstowego

def test_handle_command_too_short_v2(self): 
    simulator = MBG120Simulator() 

    with patch.multiple(simulator, 
         _send_reply=DEFAULT, 
         _reset_watchdog=DEFAULT, 
         _handle_set_watchdog=DEFAULT, 
         autospec=True) as mocks: 
     simulator._handle_command('XA99') 
     mocks['_send_reply'].assert_called_once_with('X?') 
     self.assertFalse(mocks['_reset_watchdog'].called) 
     self.assertFalse(mocks['_handle_set_watchdog'].called) 
     simulator.stop() 

z wieloma patch.object decoratorations

@patch.object(MBG120Simulator, '_send_reply', autospec=True) 
@patch.object(MBG120Simulator, '_reset_watchdog', autospec=True) 
@patch.object(MBG120Simulator, '_handle_set_watchdog', autospec=True) 
def test_handle_command_too_short_v3(self, 
            _handle_set_watchdog_mock, 
            _reset_watchdog_mock, 
            _send_reply_mock): 
    simulator = MBG120Simulator() 
    simulator._handle_command('XA99') 
    _send_reply_mock.assert_called_once_with(simulator, 'X?') 
    self.assertFalse(_reset_watchdog_mock.called) 
    self.assertFalse(_handle_set_watchdog_mock.called) 
    simulator.stop() 

metody Ręczna wymiana użyciu create_autospec

def test_handle_command_too_short_v4(self): 
    simulator = MBG120Simulator() 

    # Mock some methods. 
    simulator._send_reply = create_autospec(simulator._send_reply) 
    simulator._reset_watchdog = create_autospec(simulator._reset_watchdog) 
    simulator._handle_set_watchdog = create_autospec(simulator._handle_set_watchdog) 

    # Exercise. 
    simulator._handle_command('XA99') 

    # Check. 
    simulator._send_reply.assert_called_once_with('X?') 
    self.assertFalse(simulator._reset_watchdog.called) 
    self.assertFalse(simulator._handle_set_watchdog.called) 

Osobiście myślę, że ten ostatni jest najwyraźniejszy czytać, i nie spowoduje potwornie długich kolejkach jeżeli liczba wyśmiewanych metod rośnie. Pozwala to również uniknąć konieczności przekazywania simulator jako pierwszego argumentu (self) do assert_called_once_with.

Ale żadna z nich nie jest szczególnie miła. Zwłaszcza podejście wielokrotne patch.object, które wymaga starannego dopasowywania porządku parametrów do zagnieżdżonych dekoracji.

Czy jest jakieś podejście, które przeoczyłem, lub sposób na uczynienie tego bardziej czytelnym? Co robisz, gdy musisz załatać wiele metod na testowanej instancji/klasie?

Odpowiedz

4

Nie przegapiłeś niczego, co naprawdę różni się od tego, co zaproponowałeś.

O czytelności mój gust jest dla stylu dekoratora, ponieważ usuwa kpiny z testowanego ciała ... ale to tylko smak.

Masz rację: jeśli zainstalujesz statyczną instancję metody przez autospec=True, musisz użyć metod sprawdzania rodziny w jaźni assert_called_*. Ale twoja sprawa jest tylko małą klasą, ponieważ dokładnie wiesz, jaki obiekt chcesz załatać i nie potrzebujesz innego kontekstu dla twojej łaty niż metoda testowa.

Musisz tylko załatać swój obiekt, użyj go do całego testu: często w testach nie możesz mieć instancji do poprawienia przed wykonaniem połączenia iw tych przypadkach nie można użyć create_autospec: możesz po prostu załączyć statyczne wystąpienie metod zamiast.

Jeśli niepokoi cię przekazywanie instancji do metod assert_called_*, rozważ użycie ANY, aby przerwać zależność. W końcu napisałem setki takich testów i nigdy nie miałem problemu z kolejnością argumentów.

Moje standardowe podejście do Państwa testu jest

@patch('mbgmodule.MBG120Simulator._send_reply', autospec=True) 
@patch('mbgmodule.MBG120Simulator._reset_watchdog', autospec=True) 
@patch('mbgmodule.MBG120Simulator._handle_set_watchdog', autospec=True) 
def test_handle_command_too_short(self,mock_handle_set_watchdog, 
              mock_reset_watchdog, 
              mock_send_reply): 
    simulator = MBG120Simulator() 
    simulator._handle_command('XA99') 
    # You can use ANY instead simulator if you don't know it 
    mock_send_reply.assert_called_once_with(simulator, 'X?') 
    self.assertFalse(mock_reset_watchdog.called) 
    self.assertFalse(mock_handle_set_watchdog_mock.called) 
    simulator.stop() 
  • Łatanie jest z kodem metody badania
  • Co zaczyna makiety przez mock_ prefiksu
  • Wolę używać prostych patch połączenia i ścieżki bezwzględnej : jest jasne i czyste, co robisz:

Wreszcie: może Stwórz simulator i przestań być odpowiedzialna, a testy powinny brać pod uwagę tylko po to, aby załatać niektóre metody i przeprowadzić kontrole.

Mam nadzieję, że odpowiedź jest przydatna, ale pytanie nie ma jednoznacznej, prawidłowej odpowiedzi, ponieważ czytelność nie jest koncepcją absolutną i zależy od czytelnika. Co więcej, nawet tytuł mówiący o ogólnym przypadku, przykłady pytań dotyczą konkretnej klasy problemu, w którym należy załączyć metody obiektu do przetestowania.

[EDIT]

I choć przez chwilę o tym pytaniem i znalazłem to, co mi przeszkadza: staramy się testować i odczuć na temat metod prywatnych. Kiedy tak się stanie, pierwszą rzeczą, którą powinieneś zapytać, jest dlaczego? Istnieje wiele szans, że odpowiedź brzmi, ponieważ metody te powinny być publicznymi metodami prywatnych współpracowników (that not my words).

W tym nowym scenariuszu powinieneś wyczuć prywatnych współpracowników i nie możesz zmienić tylko swojego obiektu. Trzeba tylko załatać statyczne instancje innych klas.

+0

Bardzo dziękuję za dokładną odpowiedź. W celu: 1) Dobrze, że moja sprawa jest wyjątkowa (że mam dostęp do instancji et.c.). 2) Dzięki za napiwek o JAKIKOLWIEK. 3) Tak, być może przesadzam kruchość kolejności argumentów podczas używania dekoratorów. 4) Pytanie: Tak więc '@patch ('package.module.Class.some_method', autospec = True)' jest równoważne '@ patch.object (package.module.Class, 'some_method', autospec = True)'? To miło, a jeśli tak, to wolę twój sposób robienia tego. ... – estan

+0

... 5) Jestem nieco niezdecydowany, aby użyć 'setUp' i' tearDown', ponieważ podoba mi się, że moje testy są całkowicie niezależne, mimo że będzie to oznaczać więcej pisania. Ale to tylko osobiste preferencje. 6) .. i na tej notatce, tak przepraszam, że zadaję pytanie, które ma głównie subiektywne odpowiedzi! 7) Zastanowię się nad tym, co powiedziałeś o testowaniu prywatnych metod. W Pythonie bardzo mi się to podoba i ważne jest, aby ten kod miał 100% zasięgu linii. Ale rozważę podzielenie ich na współpracowników. Testowana klasa jest jednak niewielka. Jeszcze raz dziękuję za wspaniałą odpowiedź. – estan

+0

Gah. Przepraszamy za okropne formatowanie. Naprawdę nie jestem przyjaciółmi z funkcją komentowania SO. W każdym razie, akceptując twoją odpowiedź, ponieważ jest bardzo dobra. – estan

Powiązane problemy