I'm trying to use pytest-mock for mocking. This library is essentially a plugin/wrapper for mock and patch.
My problem is defined as:
I have an application (mymodule.py) that uses SQL Alchemy. Basically, there's a function that defines some objects from SQL Alchemy and returns a dictionary with those objects.
def some_function1():
# some code
from sqlalchemy import create_engine, MetaData, Table
engine = create_engine(f"mysql+pymysql://{username}:{password}@{host}:{port}")
meta = MetaData(engine)
my_table = Table(
'my_table',
meta,
autoload=True,
schema="some_schema"
)
db_tools = {"engine": engine, "table": my_table}
return db_tools
Then, a second function takes that output dictionary as input and uses them:
def some_function2(db_tools, data):
sql_query = db_tools["table"].insert().values(data)
db_tools["engine"].execute(sql_query)
# some more code
So now I'm writing unit tests, and I don't want to actually communicate with the real database. So I just need to mock everything sqlalchemy related. So far, I've managed to mock create_engine, MetaData and Table by doing:
mocker.patch(
'my_module.create_engine',
return_value=True
)
mocker.patch(
'my_module.MetaData',
return_value=True
)
mocker.patch(
'my_module.Table',
return_value=True
)
That allows me to test some_function1. But now I need to test some_function2, which uses the methods or attributes .insert(), .values and .execute(). How can I patch that?
There is not much benefit to mocking some_function1 as it does nothing but establish a connection to the database. It doesn't take any input and all it returns is a dictionary pointing at a table and a connection. With respect to some_function2 we can just pass in multiple MagicMock's inside the db_tools argument and use configure_mock.
def test_some_function2(mocker):
mock_table = mocker.MagicMock()
mock_engine = mocker.MagicMock()
fake_query = "INSERT blah INTO foo;"
fake_data = [2, 3]
mock_table.configure_mock(
**{
"insert.return_value": mock_table,
"values.return_value": fake_query
}
)
db_tools = {
"table": mock_table,
"engine": mock_engine
}
some_function2(db_tools, fake_data)
mock_table.insert.assert_called_once()
mock_table.values.assert_called_once_with(fake_data)
mock_engine.execute.assert_called_once_with(fake_query)
When the test is run it returns the following.
========================================================== test session starts ==========================================================
platform darwin -- Python 3.7.4, pytest-5.4.3, py-1.9.0, pluggy-0.13.1
rootdir: ***
plugins: mock-3.2.0
collected 1 item
test_foo.py . [100%]
=========================================================== 1 passed in 0.01s ===========================================================
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With