Подтвердить что ты не робот

Python, модульное тестирование и издевательский импорт

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

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

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

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

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

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

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

4b9b3361

Ответ 1

Если вы хотите импортировать модуль, в то же время гарантируя, что он ничего не импортирует, вы можете заменить встроенную функцию __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 - это список имен модулей, которые вы хотите импортировать во внутренний модуль.

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

Ответ 2

"импортирует много других файлов"? Импортирует много других файлов, которые являются частью вашей настроенной базы кода? Или импортирует много других файлов, которые являются частью дистрибутива 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) и просто импортируете его.

Или

  • Вы не доверяете чему-либо (т.е. своему собственному коду), а вы

    • проверить его отдельно и
    • высмеивать его для автономного тестирования.

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

Ответ 3

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

Ответ 4

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

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

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

value = 5

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

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

Обратите внимание, что impp входит в число глобальных символов, поскольку он был импортирован. Вызов функции 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()

Ответ 5

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