Как издеваться над mongodb, когда он вызывается из другой функции?
Мне нужна помощь во время издевательства над mongodb. Я использую Mongomock, чтобы издеваться над Mongodb.
Моя структура проекта:
- my_mongo.py
- code.py
- my_test.py
my_mongo.py имеет:
from pymongo import MongoClient
def get_db():
client = MongoClient(os.environ['MONGODB_URI'])
db = client['my_db']
return db
def insert(id, data):
return get_db().results.insert_one(
{
"id": id,
"data":df.to_json(),
}).id
и code.py имеет
import my_mongo
def action():
#do somethings
my_mongo.insert(id, data)
и my_test.py имеет
import mongomock
import my_mongo
from unittest import mock
with patch.object(my_mongo.get_db().client, "client", mongomock.MongoClient()):
import code
def test_action_1():
my_mongo.insert = mock.Mock(return_value=1)
code.action()
def test_action_2():
with patch.object(my_mongo.get_db(), "get_db", mongomock.MongoClient().db):
code.action()
Он бросает pymongo.errors.ServerSelectionTimeoutError для обоих тестов. Таким образом, он все еще входит в метод insert_one() в my_mongo.py. Я ожидаю в test_action_1 my_mongo.insert возвращает 1, но это не так.
Что мне не хватает?
1 ответ
Я не совсем уверен, что mongomock
это для, но похоже, что это для насмешки всей базы данных Монго, а не на самом деле с использованием насмешек Python. Я собираюсь ответить, не включая mongomock
так как я не думаю, что вам это действительно нужно, так что вы можете принять это за то, что оно того стоит.
Было несколько проблем:
призвание
patch.object
исправит данный метод на любом объекте, который вы ему дадите. Если вы позвонитеget_db
в тесте, тоcode.action
звонкиget_db
это 2 разных объекта. Может это работает? Но я настроен скептически, поэтому я просто изменил это.Не использовать
code
как имя вашего модуля. Это уже модуль, включенный в Python.code.action
отсутствовали аргументы и ответное заявление.
Вы также заметите, что я изменил то, как и что подвергалось насмешкам, чтобы проиллюстрировать разные способы выполнения насмешек. Тест 1 издевается insert
вызов с функцией декоратора. Тест 2 издевается get_db
позвонить с contextmanager
, Либо правильно, просто показывая, что у вас есть варианты.
Вот готовый продукт:
my_mongo.py:
from pymongo import MongoClient
def get_db():
client = MongoClient(os.environ['MONGODB_URI'])
db = client['my_db']
return db
def insert(id, data):
return get_db().results.insert_one({"id": id, "data":data.to_json()}).id # df was undefined, updated to data
my_code.py:
import my_mongo
# I added id and data args. They were undefined
def action(id, data):
return my_mongo.insert(id, data) # I added a return here
my_test.py
from unittest import mock
import my_code
# I removed the contextmanager import. Nothing is being evaluated here that would
# need to be patched, so I'm pretty certain it has no effect
@mock.patch('my_mongo.insert')
def test_action_1(mock_insert):
expected_id = 1
mock_insert.return_value = expected_id
ret = my_code.action(expected_id, mock.Mock())
assert ret == expected_id
def test_action_2():
with mock.patch('my_mongo.get_db') as mock_get_db:
expected_id = 'some id'
mock_db = mock.Mock()
mock_db.results.insert_one.return_value.id = expected_id
mock_get_db.return_value = mock_db
ret = my_code.action(expected_id, mock.Mock())
assert ret == expected_id
Эта строка кода для исправления mongodb неверна. Вместо того, чтобы использовать
patch.object(my_mongo.get_db(), "get_db", mongomock.MongoClient().db)
, вы должны использовать
patch.object("my_mongo.get_db", return_value=mongomock.MongoClient()['my_db'])
.
Ниже приведен полный исполняемый код для вашего примера:
my_test.py
import mongomock
from unittest.mock import patch
import my_code
import my_mongo
def test_action_2():
mocked_mongo = mongomock.MongoClient()
with patch("my_mongo.get_db", return_value=mongomock.MongoClient()['my_db']):
my_code.action()
assert mocked_mongo.my_db.results.count_documents({'id': 'some_id'}) == 1
my_mongo.py
from pymongo import MongoClient
def get_db():
client = MongoClient(os.environ['MONGODB_URI'])
db = client['my_db']
return db
def insert(id, data):
return get_db().results.insert_one(
{
"id": id,
"data": data,
})
my_code.py
import my_mongo
def action():
#do somethings
return my_mongo.insert('some_id', '{"a": 3}')