Reputation: 505
I want to use a mock object to emulate the database on a insert operation.
For example, let's suppose I have a method like insert(1) that calls a db connection object (let's call it db_obj) and performs some insert into mytable (col1) values (%s) where %s is 1.
What I had in mind: create some mock object for db_obj that stores the value of col1, so when db_obj.insert(1) is called, the mocked db_obj stores this col1=1 and, then, I can just get the mocked object col1 value and assert that it's 1 as expected.
Does this approach makes sense? If so, how can I do this using pytest?
Here's an example of what I'm trying
from hub.scripts.tasks.notify_job_module import http_success
from hub.scripts.tasks.notify_job_module import proceed_to
from hub.core.connector.mysql_db import MySql
from unittest.mock import patch
import logging
def proceed_to(conn,a,b,c,d,e):
conn.run_update('insert into table_abc (a,b,c,d,e) values (%s,%s,%s,%s,%s)',[a,b,c,d,e])
class MySql:
# this is the real conn implementation
def connect(self):
# ... create the database connection here
def run_update(self, query, params=None):
# ... perform some insert into the database here
class TestMySql:
# this is the mock implementation I want to get rid of
def connect(self):
pass
def run_update(self, query, params=None):
self.result = params
def get_result(self):
return self.result
def test_proceed_to():
logger = logging.getLogger("")
conn = TestMySql() ## originally, my code uses MySql() here
conn.connect()
proceed_to(conn,1,'2',3,4,5)
assert conn.get_result()[1] == 4
Please notice that I had to replace MySql() with TestMySql(), so what I've done was to implement my own Mock object manually.
This way it works, but I feel like it's obviously not the best approach here. Why?
Because we're talking about mock objects, the definition of proceed_to is irrelevant here :-) the thing is: I had to implement TestMySql.get_result() and store the data I want in self.result to get the result I want, while MySql itself does not has a get_result() itself!
What I'd like to to is to avoid having to create my own mock object here and use some smarter approach here using unittest.mock
Upvotes: 0
Views: 649
Reputation: 16815
What you are testing is basically what arguments run_update
is called with. You can just mock the connection and use assert_called_xxx methods on the mock, and if you want to check specific arguments instead of all arguments you can check call_args on the mock. Here is an example that matches your sample code:
@mock.patch("some_module.MySql")
def test_proceed_to(mocked):
# we need the mocked instance, not the class
sql_mock = mocked.return_value
conn = sql_mock.connect() # conn is now a mock - you also could have created a mock manually
proceed_to(conn, 1, '2', 3, 4, 5)
# assert that the function was called with the correct arguments
conn.run_update.assert_called_once()
# conn.run_update.assert_called_once_with() would compare all arguments
assert conn.run_update.call_args[0][1] == [1, '2', 3, 4, 5]
# call_args[0] are the positional arguments, so this checks the second positional argument
Upvotes: 1