Reputation: 534
I have a function that returns an SQLAlchemy engine object
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.
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.
AssertionError: assert Engine(sqlite://) == 'test_connection_string'
I've tried the following calls to setattr but they all fail in the same manner:
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. Does anyone have any suggestions on what I'm doing wrong?
Upvotes: 1
Views: 1740
Reputation: 534
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:
import sqlalchemy
def init_db():
return sqlalchemy.create_engine("sqlite://")
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. 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:
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.
Upvotes: 1