简体   繁体   English

如何使用 pytest monkeypatch 模拟对 SQLAlchemy create_engine 调用的调用

[英]How can I use pytest monkeypatch to mock calls to SQLAlchemy create_engine call

I have a function that returns an SQLAlchemy engine object我有一个返回 SQLAlchemy 引擎对象的函数

create_db.py: create_db.py:

from sqlalchemy import create_engine


def init_db():
    return create_engine("sqlite://", echo=True, future=True)

and I've a test that's attempting to use pytest's monkeypatch to mock the call to create_engine.我有一个测试试图使用pytest的monkeypatch来模拟对create_engine的调用。

test_db.py: test_db.py:

import sqlalchemy
from create_db import init_db


def test_correct_db_path_selected(monkeypatch):
    def mocked_create_engine():
        return "test_connection_string"

    monkeypatch.setattr(sqlalchemy, "create_engine", mocked_create_engine())
    engine = init_db()
    assert engine == "test_connection_string"

When I run pytest, the test is failing as a real sqlalchemy engine object is getting returned, not the mocked string.当我运行 pytest 时,测试失败,因为返回的是真正的 sqlalchemy 引擎对象,而不是模拟的字符串。

AssertionError: assert Engine(sqlite://) == 'test_connection_string'

I've tried the following calls to setattr but they all fail in the same manner:我尝试了以下对 setattr 的调用,但它们都以相同的方式失败:

    monkeypatch.setattr("sqlalchemy.engine.create.create_engine", mocked_create_engine)
    monkeypatch.setattr(sqlalchemy.engine.create, "create_engine", mocked_create_engine)
    monkeypatch.setattr(sqlalchemy.engine, "create_engine", mocked_create_engine)

I've gotten the basic examples from pytest docs to work but it doesn't cover a static function from a library.我已经从 pytest 文档中获得了基本示例,但它没有涵盖库中的静态函数。 Does anyone have any suggestions on what I'm doing wrong?有人对我做错了什么有任何建议吗?

So I've found a solution for my problem, but I'm still not clear on why the above code doesn't work.所以我找到了解决我的问题的方法,但我仍然不清楚为什么上面的代码不起作用。

If I change my create_db.py to directly call sqlalchemy.create_engine, the mocking function works.如果我将我的 create_db.py 更改为直接调用 sqlalchemy.create_engine,则模拟函数将起作用。

create_db.py: create_db.py:

import sqlalchemy


def init_db():
    return sqlalchemy.create_engine("sqlite://")

test_db.py: test_db.py:

import sqlalchemy
from create_db import init_db

class MockEngine:
    def __init__(self, path):
        self.path = path

def test_correct_db_path_selected(monkeypatch):
    def mocked_create_engine(path):
        return MockEngine(path=path)

    monkeypatch.setattr(sqlalchemy, "create_engine", mocked_create_engine)
    engine = init_db()
    assert engine.path == "sqlite://"

I don't mind changing my code to make it more testable but I'd still like to know if it's possible to mock a call to the original create_engine call.我不介意更改我的代码以使其更具可测试性,但我仍然想知道是否可以模拟对原始 create_engine 调用的调用。 I'll leave the question and answer up in case any else runs into the same problem.我会留下问题并回答,以防其他人遇到同样的问题。

Edit: I found a solution that doesn't involve changing the code to be tested.编辑:我找到了一个不涉及更改要测试的代码的解决方案。 The following call to setattr will mock a function call that isn't on an object:以下对 setattr 的调用将模拟不在对象上的函数调用:

monkeypatch.setattr(create_db, "create_engine", mocked_create_engine)

This works as it's telling monkeypatch to mock direct calls to create_engine in the create_db.py file.这有效,因为它告诉 monkeypatch 在 create_db.py 文件中模拟对 create_engine 的直接调用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM