New to Python pytest, below are the code snippets which I am using to mock a MongoDB connection.
My Mongo Connection util, which is used by my all dao layers:
connection_util.py
from pymongo import MongoClient
mongo_connection = MongoClient()
This is one of my DAO layers, used to get student information:
student_dao.py
from connection_util import mongo_connection
class StudentDAO:
def get_student_info(self, student_id):
student_table = mongo_connection['test_db']['student']
student_data = student_table.find_one({"_id": student_id})
return student_data
My pytest conftest file which contains the mongomock connection fixture:
conftest.py
import mongomock
import pytest
@pytest.fixture(scope='class', autouse=True)
def patch_mongo():
mongo_connection = mongomock.MongoClient()
yield mongo_connection
mongo_connection.drop_database('mongodb')
mongo_connection.drop_database('testdb')
mongo_connection.close()
My test file. Here I am trying to mock the actual mongo_connection instance with the Mongomock instance:
test_student.py
import connection_util
from student_dao import StudentDAO
@pytest.mark.usefixtures("patch_mongo")
class TestStudent:
def test_student(self, patch_mongo):
with patch.object(connection_util, "mongo_connection", patch_mongo):
student_id = "123546"
student = StudentDAO()
student_data = student.get_student_info("123546")
assert student_id == student_data.get("_id")
In the patch.object my target is connection_util, my attribute is mongo_connection and my new variable is patch_mongo(pytest fixture). It successfully mocks my mongo_connection variable to pymongo MongoClient and it works within the with statement. But in my studentdao.py it still refers to the pymongo MongoClient instead of mongomock MongoClient.
When I change the import statement of connection_util in student_dao.py from "from connection_util import mongo_connection" to "import connection_util" and change the "mongo_connection" to "connection_util.mongo_connection" it is working correctly. Below code works. It replaces the pymongo MongoClient to mongomock MongoClient.
import connection_util
class StudentDAO:
def get_student_info(self, student_id):
student_table = connection_util.mongo_connection['test_db']['student']
student_data = student_table.find_one({"_id": student_id})
return student_data
The issue here is I cannot do these changes to all my DAO layers as there are many files and doing it manually takes more time too. Is there a way to mock the mongo_connection instance without replacing the import statement?
The main problem in your test is that you patch the wrong object. This is documented in the unittest documentation under the section where to patch.
It means that if you use
from a import bin modulemoduleyou have to patch the reference ofbinmodule, e.g. patchmodule.binstead ofa.b. If you usepatch.object, that means usingpatch.object(module, "b")instead ofpatch.object(a, "b").In your case you should use something like:
Note that I also changed the import for
StudentDAOfor a similar reason. With your code, you would have created another reference ofStudentDAOin your test module, which has its own reference ofmongo_connection.Also note that your test as is does not work (you never set the student data), but I guess this is due to dumbing down the code.
Some other helpful links:
As an aside: This has been mentioned in many SO answers (including my own), and I'm not usually adding another answer. The problem with these question is the summary, that will not be found by anyone searching for a similar issue. Your question summary, however, really shows the problem, and hopefully will be found easier...