Вопрос

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

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

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

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

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

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

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

Это было полезно?

Решение

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

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

Другие советы

Если вы действительно хотите работать с механизмом импорта python, взгляните на < модуль code> ihooks . Он предоставляет инструменты для изменения поведения встроенного __ import __ . Но из твоего вопроса не понятно, зачем тебе это делать.

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

Если ваш импорт не работает, у вас есть "простой" PYTHONPATH проблема.Перенесите все ваши различные каталоги проектов в 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?Если это так, то у вас много работы.Если нет, то, возможно, вам следует издеваться только над теми вещами, которые вы на самом деле собираетесь протестировать.

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top