pythonunit-testingpytestpython-unittestmongomock

Python unit test coverage using pytest


I am new to pytest and wanted to add the below 3 methods for unit test coverage without actually using a real mongo db instance but rather mock it. Could try using a real db instance but it isn't recommended. Request for an example on how to mock mongodb client and get a document

import os
import logging
import urllib.parse
from dotenv import load_dotenv
from pymongo import MongoClient
from logger import *

load_dotenv()


def getMongoConnection():
    userName = urllib.parse.quote_plus(os.getenv("USER_NAME"))
    password = urllib.parse.quote_plus(os.getenv("PASSWORD"))
    hostName1_port = os.getenv("HOST_NAME1")
    hostName2_port = os.getenv("HOST_NAME2")
    hostName3_port = os.getenv("HOST_NAME3")
    authSourceDatabase = os.getenv("AUTH_SOURCE_DATABASE")
    replicaSet = os.getenv("REPLICA_SET")
    connectTimeoutMS = "1000"
    socketTimeoutMS = "30000"
    maxPoolSize = "100"

    try:
        client = MongoClient('mongodb://'+userName+':'+password+'@'+hostName1_port+','+hostName2_port+','+hostName3_port+'/'+authSourceDatabase+'?ssl=true&replicaSet='+replicaSet +
                             '&authSource='+authSourceDatabase+'&retryWrites=true&w=majority&connectTimeoutMS='+connectTimeoutMS+'&socketTimeoutMS='+socketTimeoutMS+'&maxPoolSize='+maxPoolSize)
        return client
    except Exception as e:
        logging.error("Error while connecting to mongoDB.")
        return False


def connectToDBCollection(client, databaseName, collectionName):
    db = client[databaseName]
    collection = db[collectionName]
    return collection


def getDoc(bucketName, databaseName, collectionName):
    try:
        client = getMongoConnection()
        if client != False:
            collection = connectToDBCollection(
                client, databaseName, collectionName)
            return collection.find_one({'bucket': bucketName})
    except Exception as e:
        logging.error("An exception occurred while fetching doc, error is ", e)

Edit : (Tried using below code and was able to cover most of the cases but seeing an error)

def test_mongo():
    db_conn = mongomock.MongoClient()
    assert isinstance(getMongoConnection(), MongoClient)


def test_connect_mongo():
    return connectToDBCollection(mongomock.MongoClient(), "sampleDB", "sampleCollection")


//trying to cover exception block for getMongoConnection()
def test_exception():
    with pytest.raises(Exception) as excinfo:
        getMongoConnection()
    assert str(excinfo.value) == False


def test_getDoc():
    collection = mongomock.MongoClient().db.collection
    stored_obj = collection.find_one({'_id': 1})
    assert stored_obj == getDoc("bucket", "db", "collection")


def test_createDoc():
    collection = mongomock.MongoClient().db.collection
    stored_obj = collection.insert_one({'_id': 1})
    assert stored_obj == createDoc("bucket", "db", "collection")


def test_updateDoc():
    collection = mongomock.MongoClient().db.collection
    stored_obj = collection.replace_one({'_id': 1}, {'_id': 2})
    assert stored_obj == updateDoc(
        {'_id': 1}, {'$set': {'_id': 2}}, "db", "collection")

Errors : test_exception - Failed: DID NOT RAISE <class 'Exception'> test_createDoc - TypeError: not all arguments converted during string formatting AssertionError: assert <pymongo.results.UpdateResult object at 0x7fc0e835a400> == <pymongo.results.UpdateResult object at 0x7fc0e8211900>


Solution

  • Looks like MongoClient is a nested dict with databaseName and collectionName or implemented with a key accessor.

    You could mock the client first with

    import unittest
    mocked_collection = unittest.mock.MagicMock()
    # mock the find_one method
    mocked_collection.find_one.return_value = {'data': 'collection_find_one_result'}
    mocked_client = unittest.mock.patch('pymongo.MongoClient').start()
    mocked_client.return_value = {
        'databaseName': {'collectionname': mocked_collection}
    }