Как пропатчить функцию в pytest, зарегистрированную на sqlalchemy event.listen "after_update"? - PullRequest
0 голосов
/ 12 июня 2018

У меня есть функция, которая зарегистрирована как событие в модели sqlalchemy, как показано в фрагментах кода ниже (не полностью функционально, так как я не показываю приспособление db), которого должно быть достаточно, чтобы объяснитьпроблема.

root / myapp / models.py:

class MyModel:
    id = Column(UUID, primary_key=True)
    value = ''

    @classmethod
    def register_hook(cls, hook_fn):
        event.listen(cls, "after_update", hook_fn, propagate=True)

root / myapp / app.py:

from models import MyModel

def hook_fn(mapper, connection, target):
    print('fired hook!')

MyModel.register_hook(hook_fn)

root / test / conftest.py:

@pytest.fixture
def patched_hook_fn(mocker):
    with mocker.patch("root.myapp.app.hook_fn") as patched:
        yield patched

root / test / tests.py:

def test_hook_fires_on_change(db, patched_hook_fn):
    model = MyModel(value="initial")
    db.session.commit()
    model.value = "changed"
    db.session.commit()  # hook fires here
    assert patched_hook_fn.called  # assert fails

Я хотел бы знать следующее:

  1. Почему не вызывается исправленная функция?

  2. Существует ли простой способсеанс отладки, чтобы увидеть, где я должен вносить исправления в строку with mocker.patch("myapp.app.hook_fn") as patched?

1 Ответ

0 голосов
/ 13 июня 2018

Не вызывается, потому что вы уже зарегистрировали непатчированную версию в системе событий.SQLAlchemy не читает значение в root.myapp.app.hook_fn каждый раз, когда происходит событие, поэтому даже если вы позже установите root.myapp.app.hook_fn = some_other_function (что и делает patch), оно не имеет видимого эффекта.

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

MyModel.register_hook(lambda: hook_fn())

Это использует способ, которым Python разрешает идентификаторы в замыкании,где изменение root.myapp.app.hook_fn фактически меняет значение hook_fn в замыкании.

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

...