2008-10-07 7 views
12

Я в проекте, где мы начинаем рефакторинг некоторой массивной базы кода. Одна из проблем, которая сразу же возникла, заключается в том, что каждый файл импортирует много других файлов. Как я изящно обманываю это в своем модульном тесте, не изменяя фактический код, поэтому я могу начать писать модульные тесты?Python, unit-testing и mocking import

В качестве примера: файл с функциями, которые я хочу протестировать, импортирует десять других файлов, которые являются частью нашего программного обеспечения, а не ядро ​​python.

Я хочу иметь возможность запускать модульные тесты как можно более индивидуально, и на данный момент я собираюсь только проверять функции, которые не зависят от вещей из импортируемых файлов.

Спасибо за все ответы.

Я не знал, с чего я хотел начать с самого начала, но теперь, думаю, знаю.

Проблема заключалась в том, что некоторые импортные операции были возможны только в том случае, если все приложение было запущено из-за какой-либо сторонней автомагии. Поэтому я должен был сделать некоторые заглушки для этих модулей в каталоге, который я указал с помощью sys.path.

Теперь я могу импортировать файл, содержащий функции, которые я хочу написать, тесты в моем тестовом файле без жалоб на отсутствующие модули.

ответ

7

Если вы хотите импортировать модуль, в то же время гарантируя, что он ничего не импортирует, вы можете заменить встроенную функцию __import__.

Например, можно использовать этот класс:

class ImportWrapper(object): 
    def __init__(self, real_import): 
     self.real_import = real_import 

    def wrapper(self, wantedModules): 
     def inner(moduleName, *args, **kwargs): 
      if moduleName in wantedModules: 
       print "IMPORTING MODULE", moduleName 
       self.real_import(*args, **kwargs) 
      else: 
       print "NOT IMPORTING MODULE", moduleName 
     return inner 

    def mock_import(self, moduleName, wantedModules): 
     __builtins__.__import__ = self.wrapper(wantedModules) 
     try: 
      __import__(moduleName, globals(), locals(), [], -1) 
     finally: 
      __builtins__.__import__ = self.real_import 

А в тестовом коде, вместо того, чтобы писать import myModule, написать:

wrapper = ImportWrapper(__import__) 
wrapper.mock_import('myModule', []) 

Второй аргумент mock_import список модулей имена, do хотите импортировать во внутренний модуль.

Этот пример может быть дополнительно изменен, например,импортируйте другой модуль, а не просто не импортируйте его, или даже издеваетесь над объектом модуля с помощью собственного пользовательского объекта.

+0

Вы хотите добавить `try: finally:` в метод mock_import, чтобы не оставлять систему с завернутым импортом вместо стандартного в случае ошибки – Yonatan 2013-02-19 08:37:02

1

«импортирует много других файлов»? Импортирует много других файлов, которые являются частью вашей настроенной базы кода? Или импортирует много других файлов, которые являются частью дистрибутива Python? Или импортирует много других файлов проекта с открытым исходным кодом?

Если ваш импорт не работает, у вас есть «простая» проблема PYTHONPAT H. Получите все ваши различные каталоги проектов на PYTHONPATH, которые вы можете использовать для тестирования. У нас есть довольно сложный путь, в Windows, мы управляем его как этим

@set Part1=c:\blah\blah\blah 
@set Part2=c:\some\other\path 
@set that=g:\shared\stuff 
set PYTHONPATH=%part1%;%part2%;%that% 

Мы держим каждый кусок пути отдельным, так что мы (а) знать, где вещи берутся и (б) может управлять изменениями, когда мы перемещаем вокруг.

Поскольку поиск PYTHONPATH осуществляется по порядку, мы можем управлять тем, что используется, регулируя порядок на пути.

Как только у вас есть «все», это становится вопросом доверия.

Либо

  • вы доверяете что-то (то есть код базового Python) и просто импортировать его.

Или

  • Вы не доверяете что-то (то есть свой собственный код), и вы

    1. тест это отдельно и
    2. дразнить его для автономного тестирования.

ли вы проверить библиотеки Python? Если это так, у вас много работы.Если нет, то, возможно, вы только издеваетесь над тем, что вы собираетесь протестировать.

1

Если вы действительно хотите обмануть механизм импорта python, посмотрите на модуль ihooks. Он предоставляет инструменты для изменения поведения встроенного __import__. Но из вашего вопроса неясно, зачем вам это нужно.

0

Не требуется сложного манипулирования, если вы хотите быстро и грязно исправить перед своими модульными испытаниями.

Если тесты устройства находятся в том же файле, что и код, который вы хотите протестировать, просто удалите ненужный модуль из словаря globals().

Вот довольно длинный пример: предположим, у вас есть модуль impp.py с содержимым:

value = 5 

Теперь в тестовом файле вы можете написать:

>>> import impp 
>>> print globals().keys() 
>>> def printVal(): 
>>>  print impp.value 
['printVal', '__builtins__', '__file__', 'impp', '__name__', '__doc__'] 

Обратите внимание, что impp является одним из globals, потому что он был импортирован. Вызов функции printVal, которая использует impp модуль все еще работает:

>>> printVal() 
5 

Но теперь, если вы удалите impp ключ из globals() ...

>>> del globals()['impp'] 
>>> print globals().keys() 
['printVal', '__builtins__', '__file__', '__name__', '__doc__'] 

... и попытаться вызвать printVal(), вы будете получить:

>>> printVal() 
Traceback (most recent call last): 
    File "test_imp.py", line 13, in <module> 
    printVal() 
    File "test_imp.py", line 5, in printVal 
    print impp.value 
NameError: global name 'impp' is not defined 

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

Чтобы использовать его в своих модульных тестах, вы можете удалить глобалы непосредственно перед запуском набора тестов, например. в __main__:

if __name__ == '__main__': 
    del globals()['impp'] 
    unittest.main() 
+0

Спасибо за ваш ответ, я многому научился у него. Но на самом деле я хочу сделать наоборот. Тесты находятся в другом файле, и когда я в этом импортирую файл, содержащий функции, которые я хочу протестировать, я хочу, чтобы этот файл думал, что все другие файлы уже импортированы. – 2008-10-07 14:59:57

0

В вашем комментарии above, вы говорите, что вы хотите, чтобы убедить питон, что некоторые модули уже были импортированы. Это по-прежнему кажется странной целью, но если это действительно то, что вы хотите сделать, в принципе вы можете прокрасться за спиной механизма импорта и изменить sys.modules. Не уверен, как это работает для импорта пакетов, но должно быть хорошо для абсолютного импорта.