assert_has_calls
est une autre approche de ce problème.
À partir de la documentation:
assert_has_calls (appels, any_order = False)
assert que le simulacre a été appelé avec les appels spécifiés. La liste mock_calls est vérifiée pour les appels.
Si any_order vaut False (valeur par défaut), les appels doivent être séquentiels. Il peut y avoir des appels supplémentaires avant ou après les appels spécifiés.
Si any_order vaut True, les appels peuvent être dans n'importe quel ordre, mais ils doivent tous apparaître dans mock_calls.
Exemple:
>>> 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)
Source: https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock.assert_has_calls
tuple
- classe :isinstance(mock.call(1), tuple)
donneTrue
. Ils ont également ajouté quelques méthodes et attributs.side_effect
Habituellement, je me fiche de l'ordre des appels, mais seulement du fait qu'ils ont eu lieu. Dans ce cas, je combine
assert_any_call
avec une assertion surcall_count
.Je trouve que faire de cette façon est plus facile à lire et à comprendre qu'une longue liste d'appels passés dans une seule méthode.
Si vous vous souciez de la commande ou que vous vous attendez à plusieurs appels identiques, cela
assert_has_calls
pourrait être plus approprié.Éditer
Depuis que j'ai publié cette réponse, j'ai repensé mon approche des tests en général. Je pense qu'il vaut la peine de mentionner que si votre test devient aussi compliqué, vous pouvez le tester de manière inappropriée ou avoir un problème de conception. Les simulacres sont conçus pour tester la communication inter-objets dans une conception orientée objet. Si votre conception n'est pas orientée objet (comme dans plus procédural ou fonctionnel), la maquette peut être totalement inappropriée. Vous pouvez également avoir trop de choses à l'intérieur de la méthode, ou vous pouvez tester des détails internes qu'il vaut mieux ne pas simuler. J'ai développé la stratégie mentionnée dans cette méthode alors que mon code n'était pas très orienté objet, et je crois que je testais également des détails internes qui auraient été mieux laissés non simulés.
la source
do() if TEST_ENV=='prod' else dont()
), est réalisé facilement en se moquant de la manière que vous avez suggérée. un effet secondaire de cela est de maintenir les tests par versions (disons les changements de code entre Google Search api v1 et v2, votre code testera la version 1 quoi qu'ilVous pouvez utiliser l'
Mock.call_args_list
attribut pour comparer des paramètres aux appels de méthode précédents. Cela en conjonction avec l'Mock.call_count
attribut devrait vous donner un contrôle total.la source
assert_has_calls
vérifie uniquement si les appels attendus ont été effectués, mais pas si ce sont les seuls.Je dois toujours regarder celui-ci encore et encore, alors voici ma réponse.
Affirmation de plusieurs appels de méthode sur différents objets de la même classe
Supposons que nous ayons une classe résistante (dont nous voulons nous moquer):
voici du code qui utilise deux instances de la
HeavyDuty
classe:Maintenant, voici un cas de test pour la
heavy_work
fonction:Nous nous moquons de la
HeavyDuty
classe avecMockHeavyDuty
. Pour affirmer les appels de méthode provenant de chaqueHeavyDuty
instance, nous devons nous référer àMockHeavyDuty.return_value.assert_has_calls
, au lieu deMockHeavyDuty.assert_has_calls
. De plus, dans la liste des,expected_calls
nous devons spécifier le nom de la méthode pour laquelle nous souhaitons affirmer les appels. Donc, notre liste est faite d'appels àcall.do_work
, plutôt que simplementcall
.L'exercice du cas de test nous montre qu'il est réussi:
Si nous modifions la
heavy_work
fonction, le test échoue et produit un message d'erreur utile:Affirmer plusieurs appels à une fonction
Pour contraster avec ce qui précède, voici un exemple qui montre comment se moquer de plusieurs appels à une fonction:
Il existe deux différences principales. Le premier est que lorsque nous nous moquons d'une fonction, nous configurons nos appels attendus en utilisant
call
plutôt qu'en utilisantcall.some_method
. La seconde est que nous appelonsassert_has_calls
aumock_work_function
lieu de continuermock_work_function.return_value
.la source