функции pytest mocking .count () и .find () объекта MongoReplicaSetClient - PullRequest
0 голосов
/ 30 апреля 2018

У меня есть скрипт с именем commons.py, который содержит некоторые наиболее часто используемые функции.

В моем основном скрипте я создаю объект соединения Монго:

db_ob = commons.db_connection()

db_connection возвращает объект соединения MongoReplicaSetClient.

как написать контрольные примеры для моей функции ниже ??

def check_entries():
    try:
        db_ob = commons.db_connection()
    except:
        print('Unable to connect to db!!')
        return False
    db_name = 'my_db_name'
    collection_name = 'my_collection_name'
    db_data = db_ob[db_name][collection_name].find()
    if db_data.count() == 0:
        print('No entries found in the collection!')
        return False
    return True

Я могу смоделировать свою функцию db_connection, но у меня возникают проблемы с имитацией функций .count () и .find ().

Ответы [ 2 ]

0 голосов
/ 08 мая 2018

Мое решение:

Поскольку функция db_connection возвращает объект, я заставил мою фиктивную функцию также возвращать объект со встроенными необходимыми функциями.

Я стал делать класс с необходимыми функциями и возвращать объект в нужном формате.

На основании различных случаев (успешное / неуспешное соединение, различные значения количества и т. Д.) Может быть несколько фиктивных функций.

def mock_db_connection_success(*args, **kwargs):
    """Function to mock successful db connection."""
    class ReturnClass(object):
        """."""
        @staticmethod
        def __init__(*args, **kwargs):
            """."""
            pass

        # @staticmethod
        def find(self, *args, **kwargs):
            """."""
            return self

        @staticmethod
        def count(*args, **kwargs):
            """."""
            return 1
    class_ob = ReturnClass()
    return {"my_db_name": {"my_collection_name": class_ob}}


@mock.patch('my_script.commons.db_connection', side_effect=mock_db_connection_success)
def test_function(*args, **kwargs):
    """Test Function."""
    # use the function here

Я также нашел реализацию того же с использованием библиотеки Python: flexmock

Используя библиотеку, нам не нужно писать полное определение для класса, и это можно сделать с помощью минимальных определений. Это помогает быстрее писать различные дела.

0 голосов
/ 30 апреля 2018

Пример с изменением курсора для проверки случая, когда набор результатов пуст:

from unittest.mock import patch, MagicMock
from pymongo.mongo_replica_set_client import MongoReplicaSetClient
from pymongo.cursor import Cursor
import testee


def test_empty_result_set():
    db_data_mock = MagicMock(spec=Cursor)()  # 1
    db_data_mock.count.return_value = 0  # 2
    db_conn_mock = MagicMock(spec=MongoReplicaSetClient)()  # 3
    db_conn_mock.__getitem__().__getitem__().find.return_value = db_data_mock
    with patch('commons.db_connection', return_value=db_conn_mock):  # 4
        assert not testee.check_entries()  # 5

подробности:

  1. MagicMock(spec=Cursor) возвращает класс, имитирующий pymongo.cursor.Cursor класс. db_data_mock является экземпляром этого фиктивного класса.
  2. db_data_mock.count.return_value = 0 проверяет метод count, поэтому он всегда возвращает ноль.

  3. Следующие две строки: создайте фиктивный экземпляр для pymongo.mongo_replica_set_client.MongoReplicaSetClient (так же, как в 1.) и подключите к нему экземпляр курсора, чтобы метод find() всегда возвращал db_data_mock экземпляр, который мы создали ранее .

  4. Наконец, замените функцию commons.db_connection на смоделированную, которая возвращает наш MongoReplicaSetClient фиктивный объект.
  5. Все приготовления сделаны; сделать реальный тест.

Обновление: тест, который не использует unittest, был запрошен в комментариях

Если по какой-то странной причине «чистоты» вы не хотите трогать код unittest, вам нужно либо найти заменяющую библиотеку для этого, либо написать макеты самостоятельно. pytest не предлагает функции насмешки из коробки. Пример выше без unittest может выглядеть так:

from collections import defaultdict
import testee


class CursorMock:

    def count(self):
        return 0


class ConnectionMock:

    def find(self):
        return CursorMock()


class MongoReplicaSetClientMock:

    def __getitem__(self, name):
        return defaultdict(ConnectionMock)


def db_connection_mock(*args, **kwargs):
    return MongoReplicaSetClientMock()


def test_empty_result_set(monkeypatch):
    monkeypatch.setattr(commons, 'db_connection', db_connection_mock)
    assert not testee.check_entries()

Вместо unittest.mock.patch использовался прибор monkeypatch .

Обратите внимание, что, хотя есть некоторые плагины для pytest, которые предлагают имитацию (например, pytest-mock), большинство из известных мне просто удобные обертки вокруг unittest.mock и все еще используют это под капотом.

...