Как издеваться над импортом
модуль A
включает в себя import B
в его верхней части. Однако в условиях тестирования я хотел бы mockB
на A
(mock A.B
) и полностью отказаться от импорта B
.
в самом деле B
не устанавливается в тестовой среде специально.
a-тестируемый модуль. Мне нужно импортировать все свои функции. B-это модуль, который мне нужно издеваться. Но как я могу издеваться над B внутри A и остановить A от импорта реального B, если первый что делает импорт б?
(причина B не установлена в том, что я использую pypy для быстрого тестирования и, к сожалению, B еще не совместим с pypy.)
как это можно сделать?
6 ответов:
вы можете назначить
sys.modules['B']
перед импортомA
чтобы получить то, что вы хотите:test.py:
import sys sys.modules['B'] = __import__('mock_B') import A print(A.B.__name__)
A.py:
import B
B. Примечание пы не существует, но при запуске
test.py
ошибка не возвращается иprint(A.B.__name__)
печатьmock_B
. Вы все еще должны создатьmock_B.py
где вы издеваетесь над фактическими функциями / переменными / и т. д. Или вы можете просто назначить макет() непосредственно:test.py:
import sys sys.modules['B'] = Mock() import A
строение
__import__
можно издеваться с помощью библиотеки "mock" для большего контроля:# Store original __import__ orig_import = __import__ # This will be the B module b_mock = mock.Mock() def import_mock(name, *args): if name == 'B': return b_mock return orig_import(name, *args) with mock.patch('__builtin__.__import__', side_effect=import_mock): import A
сказать
A
выглядит так:import B def a(): return B.func()
A.a()
возвращаетb_mock.func()
который также может быть высмеян.b_mock.func.return_value = 'spam' A.a() # returns 'spam'
как издеваться над импортом, (mock A. B)?
модуль A включает в себя импорт B в верхней части.
легко, просто издеваться над библиотекой в sys.модули перед импортом:
if wrong_platform(): sys.modules['B'] = mock.MagicMock()
а потом, пока
A
не зависит от конкретных типов данных, возвращаемых из объектов B:import A
должны просто работать.
вы также можете издеваться над
import A.B
:это работает, даже если у вас есть подмодули, но вы хотите, чтобы издеваться над каждым модулем. Скажем, у вас есть это:
from foo import This, That, andTheOtherThing from foo.bar import Yada, YadaYada from foo.baz import Blah, getBlah, boink
чтобы издеваться, просто сделайте ниже, прежде чем модуль, который содержит выше импортируется:
sys.modules['foo'] = MagicMock() sys.modules['foo.bar'] = MagicMock() sys.modules['foo.baz'] = MagicMock()
(мой опыт: у меня была зависимость, которая работает на одной платформе, Windows, но не работает на Linux, где мы запускаем наши ежедневные тесты. Поэтому мне нужно было высмеять зависимость для наших тестов. К счастью, это был черный ящик, поэтому мне не нужно было настраивать много взаимодействия.)
Насмешливый Стороны Эффекты
добавление: на самом деле, мне нужно было смоделировать побочный эффект, который занял некоторое время. Поэтому мне нужен был метод объекта, чтобы заснуть на секунду. Это будет работать так:
sys.modules['foo'] = MagicMock() sys.modules['foo.bar'] = MagicMock() sys.modules['foo.baz'] = MagicMock() # setup the side-effect: from time import sleep def sleep_one(*args): sleep(1) # this gives us the mock objects that will be used from foo.bar import MyObject my_instance = MyObject() # mock the method! my_instance.method_that_takes_time = mock.MagicMock(side_effect=sleep_one)
и затем код занимает некоторое время для запуска, так же, как реальный метод.
Я понимаю, что немного опоздал на вечеринку здесь, но вот несколько безумный способ автоматизировать это с помощью
mock
библиотека:(вот пример использования)
import contextlib import collections import mock import sys def fake_module(**args): return (collections.namedtuple('module', args.keys())(**args)) def get_patch_dict(dotted_module_path, module): patch_dict = {} module_splits = dotted_module_path.split('.') # Add our module to the patch dict patch_dict[dotted_module_path] = module # We add the rest of the fake modules in backwards while module_splits: # This adds the next level up into the patch dict which is a fake # module that points at the next level down patch_dict['.'.join(module_splits[:-1])] = fake_module( **{module_splits[-1]: patch_dict['.'.join(module_splits)]} ) module_splits = module_splits[:-1] return patch_dict with mock.patch.dict( sys.modules, get_patch_dict('herp.derp', fake_module(foo='bar')) ): import herp.derp # prints bar print herp.derp.foo
причина этого настолько смехотворно сложна, когда происходит импорт python в основном делает это (например,
from herp.derp import foo
)
- тут ? Остальное импортируйте. Если еще не
ImportError
- тут ? Остальное импортируйте. Если все еще нет
ImportError
- получить атрибут
foo
наsys.modules['herp.derp']
. ИначеImportError
foo = sys.modules['herp.derp'].foo
есть некоторые недостатки этого взломанного решения: если что-то еще полагается на другие вещи в пути модуля, это своего рода винты. И это только работает для вещей, которые импортируются inline, таких как
def foo(): import herp.derp
или
def foo(): __import__('herp.derp')
если вы делаете
import ModuleB
вы действительно вызываете встроенный метод__import__
как:ModuleB = __import__('ModuleB', globals(), locals(), [], -1)
вы можете перезаписать этот метод, импортировав
__builtin__
модуль и сделать обертку вокруг__builtin__.__import__
метод. Или вы могли бы играть сNullImporter
крюк отimp
модуль. Ловить исключение и издеваться над вашим модулем / классом вexcept
-блок.указатель на соответствующие документы:
доступ к внутренним устройствам импорта с помощью модуля imp
Я надеюсь, что это помогает. Будь очень рекомендуется, чтобы вы вступили в более тайные периметры программирования python и что a) твердое понимание того, чего вы действительно хотите достичь, и b)глубокое понимание последствий важно.
я нашел прекрасный способ издеваться над импортом в Python. Это Заади Эрика решение здесь который я просто использую внутри моего Джанго приложение.
у меня есть класс
SeatInterface
, который является интерфейсом кSeat
модель класса. Так что внутри моегоseat_interface
модуль у меня такой импорт:from ..models import Seat class SeatInterface(object): (...)
Я хотел создать изолированные тесты для
SeatInterface
класса с издевалисьSeat
классFakeSeat
. Проблема была - как ту запустить тесты в автономном режиме, где приложение Django не работает. У меня была ниже ошибка:неправильно сконфигурировано: запрошена настройка BASE_DIR, но настройки не заданы сконфигурированный. Необходимо либо определить переменную среды DJANGO_SETTINGS_MODULE или настройки вызова.настройка () перед доступом настройки.
пробежал 1 Тест за 0,078 с
ошибка (ошибки=1)
решение:
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
а затем тест волшебным образом работает ОК :)
.
Пробежал 1 Тест за 0,002 сОК