J'ai besoin de créer une sorte de MockMixin
pour mes tests. Il doit inclure des simulations pour tout ce qui appelle des sources externes. Par exemple, chaque fois que j'enregistre le modèle dans le panneau d'administration, j'appelle des URL distantes. Ce serait bien de se moquer de ça et de l'utiliser comme ça:
class ExampleTestCase(MockedTestCase):
# tests
Ainsi, chaque fois que j'enregistre le modèle dans admin, par exemple dans les tests fonctionnels, cette maquette est appliquée au lieu d'appeler des URL distantes.
Est-ce vraiment possible? Je suis capable de le faire pour 1 test particulier, ce n'est pas un problème. Mais il serait plus utile d'avoir une maquette globale parce que je l'utilise beaucoup.
Selon la documentation mock
:
Le patch peut être utilisé comme décorateur de classe TestCase. Il fonctionne en décorant chaque méthode de test de la classe. Cela réduit le code passe-partout lorsque vos méthodes de test partagent un ensemble de correctifs commun.
Cela signifie essentiellement que vous pouvez créer une classe de test de base avec un décorateur @patch
Qui se moquerait de vos appels externes tandis que chaque méthode de test à l'intérieur serait exécutée.
Vous pouvez également utiliser start()
et stop()
les méthodes de patcher dans les méthodes setUp()
et tearDown()
respectivement:
class BaseTestCase(TestCase):
def setUp(self):
self.patcher = patch('mymodule.foo')
self.mock_foo = self.patcher.start()
def tearDown(self):
self.patcher.stop()
Juste pour ajouter à la réponse d'alecxe , si vous utilisez teardown()
alors selon la documentation
vous devez vous assurer que le correctif est "annulé" en appelant
stop
. Cela peut être plus compliqué que vous ne le pensez, car si une exception est déclenchée danssetUp
, alorstearDown
n'est pas appelée.
Si une exception est levée dans vos tests, votre correction ne sera pas annulée. Une meilleure façon serait d'appeler addCleanup()
à l'intérieur de votre setUp()
. Vous pouvez ensuite omettre complètement la méthode tearDown()
.
class BaseTestCase(TestCase):
def setUp(self):
self.patcher = patch('mymodule.foo')
self.mock_foo = self.patcher.start()
self.addCleanup(self.patcher.stop) # add this line
J'ai fini par créer un testeur pour servir mon objectif. Je devais simuler le stockage de fichiers afin que les images n'écrivent pas réellement dans le système de fichiers pendant le test. L'objet images est appelé dans de nombreux tests, donc patcher chaque classe ne serait pas DRY
. De plus, j'ai remarqué que se moquer du fichier lui-même le laisserait sur le système au cas où le test échouerait. Mais cette méthode n'a pas fonctionné.
J'ai créé un fichier runner.py
dans la racine du projet
# runner.py
from unittest.mock import patch
from Django.test.runner import DiscoverRunner
from myapp.factories import ImageFactory
class UnitTestRunner(DiscoverRunner):
@patch('Django.core.files.storage.FileSystemStorage.save')
def run_tests(self, test_labels, mock_save, extra_tests=None, **kwargs):
mock_save.return_value = ImageFactory.get_image()
return super().run_tests(test_labels, extra_tests=None, **kwargs)
Ensuite, j'exécutais mes tests en utilisant python manage.py tests --testrunner=runner.UnitTestRunner
Pour plus de clarté, le ImageFactory.get_image
méthode est une méthode personnalisée
from Django.core.files.base import ContentFile
from factory.Django import DjangoModelFactory
from io import BytesIO
from PIL import Image as PilImage
from random import randint
class ImageFactory(DjangoModelFactory):
@classmethod
def get_image(cls, name='trial', extension='png', size=None):
if size is None:
width = randint(20, 1000)
height = randint(20, 1000)
size = (width, height)
color = (256, 0, 0)
file_obj = BytesIO()
image = PilImage.new("RGBA", size=size, color=color)
image.save(file_obj, extension)
file_obj.seek(0)
return ContentFile(file_obj.read(), f'{name}.{extension}')