190 votes

Comment simuler une importation

Module A comprend import B à son sommet. Cependant, dans des conditions de test, j'aimerais simuler B en A (moquerie A.B ) et s'abstenir totalement d'importer B .

En fait, B n'est pas installé dans l'environnement de test à dessein.

A est l'unité testée. Je dois importer A avec toutes ses fonctionnalités. B est le module que je dois simuler. Mais comment puis-je simuler B sur A et arrêter A d'importer le vrai B si la première chose A fait est d'importer B ?

(La raison pour laquelle B n'est pas installé est que j'utilise pypy pour des tests rapides et malheureusement B n'est pas encore compatible avec pypy).

Comment cela pourrait-il être fait ?

5voto

Hunter_71 Points 447

J'ai trouvé un bon moyen de simuler les importations en Python. C'est Zaadi d'Eric solution trouvée aquí que j'utilise simplement dans mon Django application.

J'ai de la classe SeatInterface qui est une interface pour Seat classe de modèle. Ainsi, dans ma seat_interface module j'ai une telle importation :

from ..models import Seat

class SeatInterface(object):
    (...)

Je voulais créer des tests isolés pour SeatInterface avec une classe simulée Seat comme FakeSeat . Le problème était de savoir comment exécuter les tests hors ligne, lorsque l'application Django est hors service. J'avais l'erreur suivante :

ImproperlyConfigured : Le paramètre demandé BASE_DIR, mais les paramètres ne sont pas configurés. Vous devez soit définir la variable d'environnement DJANGO_SETTINGS_MODULE ou appeler settings.configure() avant d'accéder aux paramètres.

Exécuté 1 test en 0.078s

FAILED (erreurs=1)

La solution était :

import unittest
from mock import MagicMock, patch

class FakeSeat(object):
    pass

class TestSeatInterface(unittest.TestCase):

    def setUp(self):
        models_mock = MagicMock()
        models_mock.Seat.return_value = FakeSeat
        modules = {'app.app.models': models_mock}
        patch.dict('sys.modules', modules).start()

    def test1(self):
        from app.app.models_interface.seat_interface import SeatInterface

Et ensuite, comme par magie, le test se déroule correctement :)

.
Exécuté 1 test en 0.002s

OK

3voto

Don Question Points 3856

Si vous faites un import ModuleB vous appelez en réalité la méthode intégrée __import__ comme :

ModuleB = __import__('ModuleB', globals(), locals(), [], -1)

Vous pouvez écraser cette méthode en important le fichier __builtin__ et créer une enveloppe autour du module __builtin__.__import__ méthode. Vous pouvez aussi jouer avec la méthode NullImporter à partir du imp module. En attrapant l'exception et en simulant votre module/classe dans le module except -bloc.

Pointeur vers les documents pertinents :

docs.python.org : __import__

Accéder aux internes d'importation avec le module imp

J'espère que cela vous aidera. Soyez HAUTEMENT Je vous conseille de vous lancer dans les périmètres les plus obscurs de la programmation python et que a) une solide compréhension de ce que vous voulez vraiment réaliser et b) une compréhension approfondie des implications sont importantes.

3voto

Tgsmith61591 Points 1507

Je sais qu'il s'agit d'une question assez ancienne, mais je me suis retrouvé à y revenir plusieurs fois récemment, et je voulais partager une solution concise à ce sujet.

import sys
from unittest import mock

def mock_module_import(module):
    def _outer_wrapper(func):
        def _inner_wrapper(*args, **kwargs):
            orig = sys.modules.get(module)  # get the original module, if present
            sys.modules[module] = mock.MagicMock()  # patch it
            try:
                return func(*args, **kwargs)
            finally:
                if orig is not None:  # if the module was installed, restore patch
                    sys.modules[module] = orig
                else:  # if the module never existed, remove the key
                    del sys.modules[module]
        return _inner_wrapper
    return _outer_wrapper

Il fonctionne en Parcheando temporairement la clé pour le module en sys.modules puis de restaurer le module d'origine après l'appel de la fonction décorée. Cela peut être utilisé dans des scénarios où un paquet peut ne pas être installé dans l'environnement de test, ou dans un scénario plus complexe où le module patché pourrait en fait effectuer certains de ses propres monkey-Parcheando internes (ce qui était le cas auquel j'étais confronté).

Voici un exemple d'utilisation :

@mock_module_import("some_module")
def test_foo():
    assert True

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X