Pytest крепление для метакласса - PullRequest
0 голосов
/ 20 февраля 2019

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

settings.py:

SETTING_1 = True
SETTING_2 = False
...

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

Я не нашел что-то об этом в документации.

1 Ответ

0 голосов
/ 21 февраля 2019

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

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

Но если вы поместите эти элементы в тела функций, они будут перестроены при вызове функций.Если они являются функциями фикстуры, то каждый раз, когда используется фикстура, они перестраиваются (хотя я думаю, что Pytest выполняет некоторое кэширование фикстур - остерегайтесь этого)

С другой стороны, если метаклассы и классы, которые используют затем,находятся в коде приложения, тогда вы не можете поместить их в тела функций для воссоздания.В этом случае вам придется тщательно спроектировать использование функции imp.reload - это позволит повторно запускать код на уровне модуля при каждом вызове.Вы можете вызывать imp.reload(<module>) внутри функций своего прибора, но будьте осторожны, чтобы повторно связать любые ссылки на эти метаклассы (и классы, которые их используют) во время работы прибора.

Итак, если код вашего приложения что-то не так:

# module_b
from module_a import SETTING_1, SETTING_2

class Meta(type):
    def __init__(mcls, name, bases, ns, **kw):
         # code that make use of SETTING_1 and SETTING_2
         ...

class A(metaclass=Meta):
    ...

модуль тестовых приборов:

import pytest
from unittest import mock

@pytest.fixture
def recreate_metaclass():
    from imp import reload
    # Drawback: fixtures file have to know and manually imported here 
    import test_module_1, ...
    import module_b
    with mock.patch("module_a.SETTING_1", val_1), mock.patch("module_a.SETTING_2" val_2):
        module_b = reload(module_b)
        # rebind module_a inside test modules
        test_module_1.module_b = module_b
        ...
...