assert_has_calls
to inne podejście do tego problemu.
Z dokumentów:
assert_has_calls (połączenia, any_order = False)
potwierdzić, że mock został wywołany z określonymi wywołaniami. Lista mock_calls jest sprawdzana pod kątem połączeń.
Jeśli any_order ma wartość False (wartość domyślna), wywołania muszą być sekwencyjne. Mogą być dodatkowe połączenia przed lub po określonych połączeniach.
Jeśli any_order ma wartość True, wtedy wywołania mogą być w dowolnej kolejności, ale wszystkie muszą pojawić się w mock_calls.
Przykład:
>>> from unittest.mock import call, Mock
>>> mock = Mock(return_value=None)
>>> mock(1)
>>> mock(2)
>>> mock(3)
>>> mock(4)
>>> calls = [call(2), call(3)]
>>> mock.assert_has_calls(calls)
>>> calls = [call(4), call(2), call(3)]
>>> mock.assert_has_calls(calls, any_order=True)
Źródło: https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock.assert_has_calls
tuple
:isinstance(mock.call(1), tuple)
dajeTrue
. Dodali także kilka metod i atrybutów.side_effect
Zwykle nie obchodzi mnie kolejność połączeń, tylko to, że się wydarzyły. W takim przypadku łączę się
assert_any_call
z twierdzeniem ocall_count
.Uważam, że zrobienie tego w ten sposób jest łatwiejsze do odczytania i zrozumienia niż duża lista wywołań przekazywana do jednej metody.
Jeśli zależy Ci na porządku lub spodziewasz się wielu identycznych połączeń,
assert_has_calls
może być bardziej odpowiednie.Edytować
Odkąd opublikowałem tę odpowiedź, przemyślałem ogólnie swoje podejście do testowania. Myślę, że warto wspomnieć, że jeśli twój test staje się tak skomplikowany, możesz testować niewłaściwie lub masz problem z projektem. Makiety są przeznaczone do testowania komunikacji między obiektami w projekcie zorientowanym obiektowo. Jeśli Twój projekt nie jest zorientowany na zastrzeżenia (np. Bardziej proceduralny lub funkcjonalny), próba może być całkowicie nieodpowiednia. Być może w metodzie dzieje się zbyt wiele lub możesz testować wewnętrzne szczegóły, które najlepiej pozostawić niezablokowane. Opracowałem strategię wspomnianą w tej metodzie, gdy mój kod nie był zbyt zorientowany obiektowo i uważam, że testowałem również wewnętrzne szczegóły, które najlepiej byłoby pozostawić niezamokowane.
źródło
do() if TEST_ENV=='prod' else dont()
), można łatwo osiągnąć przez kpienie z zasugerowanego sposobu. efektem ubocznym jest utrzymanie testów na wersje (powiedzmy, że zmiany kodu między API wyszukiwarki Google v1 i v2, Twój kod będzie testował wersję 1 bez względu na wszystko)Możesz użyć
Mock.call_args_list
atrybutu, aby porównać parametry z poprzednimi wywołaniami metod. To w połączeniu zMock.call_count
atrybutem powinno dać Ci pełną kontrolę.źródło
assert_has_calls
sprawdza tylko, czy oczekiwane wywołania zostały wykonane, ale nie jeśli są jedynymi.Zawsze muszę to raz sprawdzać, więc oto moja odpowiedź.
Asserting wielu wywołań metod na różnych obiektach tej samej klasy
Załóżmy, że mamy klasę heavy duty (którą chcemy kpić):
tutaj jest kod, który używa dwóch instancji
HeavyDuty
klasy:Oto przypadek testowy dla
heavy_work
funkcji:Kpimy z
HeavyDuty
klasyMockHeavyDuty
. Aby potwierdzić wywołania metod pochodzące z każdejHeavyDuty
instancji, musimy odwoływać sięMockHeavyDuty.return_value.assert_has_calls
zamiastMockHeavyDuty.assert_has_calls
. Ponadto na liścieexpected_calls
musimy określić, dla której nazwy metody jesteśmy zainteresowani potwierdzaniem wywołań. Tak więc nasza lista składa się z połączeń docall.do_work
, a nie po prostucall
.Ćwiczenie przypadku testowego pokazuje nam, że się udało:
Jeśli zmodyfikujemy
heavy_work
funkcję, test zakończy się niepowodzeniem i wyświetli pomocny komunikat o błędzie:Potwierdzanie wielu wywołań funkcji
W przeciwieństwie do powyższego, oto przykład, który pokazuje, jak mockować wiele wywołań funkcji:
Istnieją dwie główne różnice. Pierwszą z nich jest to, że podczas mockowania funkcji konfigurujemy nasze oczekiwane wywołania za pomocą
call
zamiast używaćcall.some_method
. Drugim jest to, że możemy zadzwonićassert_has_calls
namock_work_function
, zamiast namock_work_function.return_value
.źródło