Les points clés qui expliquent la différence et donnent des conseils pour travailler avec unittest.mock
- Utilisez Mock si vous voulez remplacer certains éléments d'interface (en passant les args) de l'objet à tester.
- Utilisez le patch si vous voulez remplacer l'appel interne à certains objets et modules importés de l'objet testé.
-
Toujours fournir la spécification de l'objet dont vous vous moquez.
- Avec patch, vous pouvez toujours fournir autospec
- Avec Mock, vous pouvez fournir spec
- Au lieu de Mock, vous pouvez utiliser créer_autospec qui vise à créer des objets Mock avec des spécifications.
Dans la question ci-dessus, la bonne réponse serait d'utiliser Mock
ou pour être plus précis create_autospec
(parce qu'elle ajoutera une spécification aux méthodes fantaisie de la classe fantaisie), la définition de l'option spec
sur le mock sera utile dans le cas d'une tentative d'appel d'une méthode de la classe qui n'existe pas (signature quelconque), veuillez voir quelques
from unittest import TestCase
from unittest.mock import Mock, create_autospec, patch
class MyClass:
@staticmethod
def method(foo, bar):
print(foo)
def something(some_class: MyClass):
arg = 1
# Would fail becuase of wrong parameters passed to methd.
return some_class.method(arg)
def second(some_class: MyClass):
arg = 1
return some_class.unexisted_method(arg)
class TestSomethingTestCase(TestCase):
def test_something_with_autospec(self):
mock = create_autospec(MyClass)
mock.method.return_value = True
# Fails because of signature misuse.
result = something(mock)
self.assertTrue(result)
self.assertTrue(mock.method.called)
def test_something(self):
mock = Mock() # Note that Mock(spec=MyClass) will also pass, because signatures of mock don't have spec.
mock.method.return_value = True
result = something(mock)
self.assertTrue(result)
self.assertTrue(mock.method.called)
def test_second_with_patch_autospec(self):
with patch(f'{__name__}.MyClass', autospec=True) as mock:
# Fails because of signature misuse.
result = second(mock)
self.assertTrue(result)
self.assertTrue(mock.unexisted_method.called)
class TestSecondTestCase(TestCase):
def test_second_with_autospec(self):
mock = Mock(spec=MyClass)
# Fails because of signature misuse.
result = second(mock)
self.assertTrue(result)
self.assertTrue(mock.unexisted_method.called)
def test_second_with_patch_autospec(self):
with patch(f'{__name__}.MyClass', autospec=True) as mock:
# Fails because of signature misuse.
result = second(mock)
self.assertTrue(result)
self.assertTrue(mock.unexisted_method.called)
def test_second(self):
mock = Mock()
mock.unexisted_method.return_value = True
result = second(mock)
self.assertTrue(result)
self.assertTrue(mock.unexisted_method.called)
Les cas de test avec les spécifications définies utilisées échouer car les méthodes appelées par something
y second
fonctions ne sont pas des plaintes avec MyClass, ce qui signifie - qu'ils attrapent les bogues, alors que les valeurs par défaut Mock
s'affichera.
Il existe encore une autre option : utiliser la fonction patch.objet pour simuler uniquement la méthode de la classe qui est appelée avec.
Les bons cas d'utilisation du patch sont ceux où la classe est utilisée comme partie interne d'une fonction :
def something():
arg = 1
return MyClass.method(arg)
Ensuite, vous voudrez utiliser patch comme décorateur pour simuler la MyClass.
20 votes
En tant que personne n'ayant jamais essayé ni Mock() ni patch, j'ai l'impression que la première version est plus claire et montre ce que vous voulez faire, même si je ne comprends pas la différence réelle. Je ne sais pas si cela peut aider ou non, mais j'ai pensé qu'il pourrait être utile de transmettre ce qu'un programmeur non initié pourrait ressentir.
2 votes
@MichaelBrennan : Merci pour votre commentaire. Il est en effet utile.