Использование поддельной mongoDB для тестирования на pytest

1

У меня есть код, который подключается к клиенту 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 db будет использоваться в mongomock тестирования. Я также попытался изменить setattr на monkeypatch.setattr('project.working_class.mongo_stuff.mongo', fake_mongo) который также не работает.

Теги:
pymongo
fixtures
mongomock
pytest

1 ответ

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

Вам лучше избегать запуска кода на уровне модуля, если это возможно, или запустить импорт, который выполняет код на уровне модуля внутри тестов (как вы уже узнали в комментариях).

  • 0
    Это все еще не работает. Я думаю, что это связано с путем импорта, который выглядит так, как описано здесь
  • 0
    Если я делаю импорт внутри тестовой функции, она, кажется, работает.
Показать ещё 4 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню