Reputation: 1245
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?
Upvotes: 0
Views: 597
Reputation: 16855
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 b
in module module
you have to patch the reference of b
in module
, e.g. patch module.b
instead of a.b
. If you use patch.object
, that means using patch.object(module, "b")
instead of patch.object(a, "b")
.
In your case you should use something like:
from patch_mongo import student_dao
class TestStudent:
def test_student(self, patch_mongo):
with patch.object(student_dao, "mongo_connection", patch_mongo):
student = student_dao.StudentDAO()
...
Note that I also changed the import for StudentDAO
for a similar reason. With your code, you would have created another reference of StudentDAO
in your test module, which has its own reference of mongo_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...
Upvotes: 0