Как я могу вставить объект в другое пространство имен в Python? - PullRequest
4 голосов
/ 20 августа 2010

Я пишу несколько юнит-тестов для кода, написанного кем-то еще здесь, в офисе. Python не мой самый сильный язык. В то время как я успешно справлялся с базовыми юнит-тестами, насмешка над python бросает меня за петлю.

Что мне нужно сделать, так это переопределить вызов ConfigObj и внедрить мою собственную фиктивную конфигурацию / приспособление в любой вызов ConfigObj.

settings.py

from configobj import ConfigObj
config = ConfigObj('/etc/myapp/config')

utils.py

from settings import config
"""lots of stuff methods using various config values."""

Что я хотел бы сделать, так это в моих юнит-тестах для utils.py ввести себя для ЛЮБОГО вызова ConfigObj или самого settings.py.

Многие из насмешливых библиотек ожидают, что я буду издеваться над моими собственными классами, но в случае этого приложения у него нет явных классов.

Можно ли это сделать или ограничения пространства имен python слишком строги, чтобы я не мог вмешиваться в то, что импортирует сам импортируемый модуль?

Примечание: запуск 2.7, поэтому я не могу выполнить ни одного трюка, о котором я читал в 2.5.

Ответы [ 5 ]

2 голосов
/ 21 августа 2010

Если тесты находятся в отдельном файле из settings.py и utils.py, вы можете создать файл mock.py

import configobj

class MockConfigObj(object):
     #mock whatever you wan

configobj.ConfigObj = MockConfigObj

и затем import mock перед импортом (из) любого модуля, который сам импортирует settings. Это обеспечит создание settings.config с MockConfigObj. Если вам нужен унифицированный глобальный макет, импортируйте его перед любым файлом, который импортирует configobj.

Это работает, потому что python будет хранить configobj в sys.modules и проверять это перед фактическим чтением из файла при последующем импорте. в mock.py идентификатор ConfigObj является просто ссылкой на запись в sys.modules, так что любые сделанные вами изменения будут видны во всем мире.

Это кажется мне немного хакерским, но это лучшее, что я могу придумать.

1 голос
/ 21 августа 2010

Пространства имен Python вообще не являются строгими в одной и той же области видимости.Просто переопределите имя переменной, содержащей ваш объект (или сам класс и предоставил его) в той же области, в которой вы ожидаете оригинала, и это достаточно хорошо.

Теперь, независимо от того, что вы заменяетеэто же ведет себя так же зависит от вас ...

0 голосов
/ 17 июня 2014

Следующая страница хороша для насмешек и импорта

http://www.relaxdiego.com/2014/04/mocking-objects-in-python.html

Скажем, у вас есть файл my_package1.py со следующим кодом:

class A(object): def <strong>init</strong>(self):

и затем вы импортируете это в my_package2.py с кодом

from my_package1 import A class A(object): def <strong>init</strong>(self):

Первая строка my_package2.py создает переменную в пространстве имен my_package2 с именемA. Теперь у вас есть две переменные my_package1.A и my_package2.A, которые указывают на один и тот же класс в памяти.Если вы хотите, чтобы код в my_package2.py использовал макетированный класс A, вам нужно будет смоделировать my_package2.A, а не my_package1.A

0 голосов
/ 21 августа 2010

Я сталкивался с подобной ситуацией раньше. Вот как бы я решил вашу проблему.

Рассмотрим тестовый пример для функции из utils.py.

import utils, unittest

class FooFunctionTests(unittest.TestCase):
    def setUp(self):
        utils._old_config = utils.config
        utils.config = MockClass()

    def tearDown(self):
        utils.config = utils._old_config
        del utils._old_config

    def test_foo_function_returns_correct_value(self):        
        self.assertEqual("success!", utils.foo())
0 голосов
/ 21 августа 2010

Не могли бы вы просто переписать исходную функцию другой?

В Python нет констант, вы можете изменить все, вы даже можете сделать True = False.

...