Перезапустите python (или перезагрузите модули) в тестах py.test

У меня есть пакет (python3), который имеет совершенно другое поведение в зависимости от того, как это init()ed (возможно, не лучший дизайн, но переписывание не вариант). Модуль может быть только init()Один раз, второй раз выдает ошибку. Я хочу протестировать этот пакет (оба поведения), используя py.test.

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

У меня есть несколько test_xxx.py модули в моей тестовой директории. Каждый модуль будет инициализировать пакет так, как ему нужно (используя приспособления). поскольку py.test запускает интерпретатор python один раз, сбой всех тестовых модулей за один запуск py.test.

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

  • Можно ли сказать py.test запускать каждый тестовый модуль в отдельном процессе python (таким образом, на него не влияют inits другого тестового модуля)
  • Есть ли способ надежно перезагрузить пакет (включая все подчиненные и т. Д.)?
  • Есть ли другое решение (я думаю об импорте, а затем об импорте пакета в приспособлении, но это кажется чрезмерным)?

4 ответа

Чтобы перезагрузить модуль, попробуйте использовать reload() из библиотеки imp

Пример:

from imp import reload
import some_lib
#do something
reload(some_lib) 

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

пример

import some_test
from multiprocessing import Manager, Process

#create new return value holder, in this case a list
manager = Manager()
return_value = manager.list()
#create new process
process =  Process(target=some_test.some_function, args=(arg, return_value))
#execute process
process.start()
#finish and return process
process.join()
#you can now use your return value as if it were a normal list, 
#as long as it was assigned in your subprocess

Удалите весь импорт модулей, а также импорт тестов, которые также импортируют ваши модули:

      import sys

for key in list(sys.modules.keys()):
    if key.startswith("your_package_name") or key.startswith("test"):
        del sys.modules[key]

вы можете использовать это как приспособление, настроив на своем conftest.py запилить приспособление, используя @pytest.fixture декоратор.

Однажды у меня была похожая проблема, правда, довольно плохой дизайн..

@pytest.fixture()
def module_type1():
    mod = importlib.import_module('example')
    mod._init(10)
    yield mod
    del sys.modules['example']

@pytest.fixture()
def module_type2():
    mod = importlib.import_module('example')
    mod._init(20)
    yield mod
    del sys.modules['example']


def test1(module_type1)
    pass

def test2(module_type2)
    pass

В примере /init.py было что-то вроде этого

def _init(val):
    if 'sample' in globals():
        logger.info(f'example already imported, val{sample}' )
    else:
        globals()['sample'] = val
        logger.info(f'importing example with val : {val}')

выход:

importing example with val : 10
importing example with val : 20

Понятия не имею, насколько сложен ваш пакет, но если это просто глобальные переменные, то это, вероятно, поможет.

У меня такая же проблема, и я нашел три решения:

  1. перезагрузить (some_lib)
  2. patch SUT, поскольку импортированный метод является ключом и значением в SUT, вы можете исправить SUT. Например, если вы используете f2 из m2 в m1, вы можете пропатчить m1.f2 вместо m2.f2
  3. import module и используйте module.function.
Другие вопросы по тегам