Использование поддельной mongoDB для тестирования на pytest
У меня есть код, который подключается к клиенту MongoDB, и я пытаюсь проверить его. Для тестирования я не хочу подключаться к реальному клиенту, поэтому я пытаюсь найти фальшивый для тестирования. Основной поток кода у меня есть функция где-то создает pymongo
клиент, затем запрашивает это и делает dict, который используется в другом месте.
Я хочу написать несколько тестов с использованием pytest, которые будут тестировать различные функции и классы, которые будут вызывать get_stuff
, Моя проблема в том что get_stuff
звонки mongo()
что на самом деле делает подключение к базе данных. Я пытался просто использовать pytest.fixture(autouse=True)
а также mongomock.MongoClient()
заменить mongo()
,
Но это не заменяет mongo_stuff.mongo()
, Есть ли какой-нибудь способ, которым я могу сказать pytest, чтобы заменить функцию, чтобы мой fixture
вызывается вместо фактической функции? Я думал сделать fixture
поставил бы мое тестирование mongo()
более высокий приоритет в пространстве имен, чем функция в реальном модуле.
Вот пример структуры файла с моим примером:
.
├── project
│ ├── __init__.py
│ ├── mongo_stuff
│ │ ├── __init__.py
│ │ └── mongo_stuff.py
│ └── working_class
│ ├── __init__.py
│ └── somewhere_else.py
└── testing
├── __init__.py
└── test_stuff.py
mongo_stuff.py
import pymongo
def mongo():
return pymongo.MongoClient(connection_params)
def get_stuff():
db = mongo() # Makes the connection using another function
stuff = query_function(db) # Does the query and makes a dict
return result
somewhere_else.py
from project.mongo_stuff import mongo_stuff
mongo_dict = mongo_stuff.get_stuff()
test_stuff.py
import pytest
import mongomock
@pytest.fixture(autouse=True)
def patch_mongo(monkeypatch):
db = mongomock.MongoClient()
def fake_mongo():
return db
monkeypatch.setattr('project.mongo_stuff.mongo', fake_mongo)
from poject.working_class import working_class # This starts by calling project.mongo_stuff.mongo_stuff.get_stuff()
И это в настоящее время даст мне ошибку подключения, так как connection params
в mongo_stuff.py предназначены только для работы в производственной среде. Если я поставлю import
оператор из test_stuff.py в тестовую функцию, то он работает нормально и mongomock
дБ будет использоваться в тестовой среде. Я также пытался изменить setattr
в monkeypatch.setattr('project.working_class.mongo_stuff.mongo', fake_mongo)
что тоже не работает.
1 ответ
Вы на полпути: вы создали макет для клиента db, теперь вы должны исправить mongo_stuff.mongo
Функция для возврата макета вместо реального соединения:
@pytest.fixture(autouse=True)
def patch_mongo(monkeypatch):
db = mongomock.MongoClient()
def fake_mongo():
return db
monkeypatch.setattr('mongo_stuff.mongo', fake_mongo)
Редактировать:
Причиной возникновения ошибки подключения является то, что вы импортируете somewhere_else
на уровне модуля в test_stuff
, а также somewhere_else
запускает код подключения также на уровне модуля. Так что исправление с помощью приспособлений придет слишком поздно и не будет иметь никакого эффекта. Вы должны исправить клиент Монго перед импортом somewhere_else
если вы хотите импортировать на уровне модуля. Это позволит избежать появления ошибки, но крайне уродливо:
from project.mongo_stuff import mongo_stuff
import mongomock
import pytest
from unittest.mock import patch
with patch.object(mongo_stuff, 'mongo', return_value=mongomock.MongoClient()):
from project.working_class import somewhere_else
@patch.object(mongo_stuff, 'mongo', return_value=mongomock.MongoClient())
def test_db1(mocked_mongo):
mongo_stuff.mongo()
assert True
@patch.object(mongo_stuff, 'mongo', return_value=mongomock.MongoClient())
def test_db2(mocked_mongo):
somewhere_else.foo()
assert True
Вы должны избегать выполнения кода на уровне модуля, когда это возможно, или запускать импорт, который выполняет код на уровне модуля внутри тестов (как вы уже узнали в комментариях).