In one of our projects, we needed to test some mongo based backend functionality: we wrote a small application which comprised of a mongo backend and a python app which communicated with the backend via pymongo. We like the flexibility of mongo in a rapid prototyping context and did not want to go with a full fledged ORM model for this app. Here we describe how we used MockupDB to perform some unit testing on this app.
PyMongo is the official Python driver for connecting Mongo databases: it has a full set of features for managing documents and also a large set of tools and frameworks that can be used in conjunction with PyMongo. Despite this rich ecosystem, there is little documentation on best practices for testing PyMongo applications – mongobox is provided as a framework tool for testing; its model is one in which a MongoDB instance is created with a temporary directory to store database files for testing – this solution can be heavy for some types of testing, unit testing in particular.
Just recently when looking for approaches to testing our web-based application we found MockupDB, a wire protocol server for MongoDB. MockupDB fits pretty much our testing requirements – we needed to test mostly CRUD operations in the REST API interface of our application, which connects to a mongo database and reads or modifies the content of documents before serving them. But MockupDB goes beyond that, allowing testing pattern-matching, cursor failure, request failure and much more.
Here is an example of a test used as starting point for our application (it uses the standard python testing conventions).
# test_database.py
from mockupdb import go, MockupDB
import unittest
Import datetime
# Our own database class using PyMongo
from database import MongoDatabase
DOCUMENT = {"_id": "123ab-456cd-789ef", 'name': "abc123",
"flavor_id": 1, "image_id": 2, "job_duration": 120}
class MockupDBDatabaseTest(unittest.TestCase):
def setUp(self):
self.server = MockupDB(auto_ismaster={"maxWireVersion": 3})
self.server.run()
# Replace pymongo connection url to use MockupDB
self.database = MongoDatabase(self.server.uri)
def tearDown(self):
self.server.stop()
def test_list_documents(self):
document = DOCUMENT
# adding datetime object to document
document['request_time'] = datetime.datetime.now()
# The database list method converts any datetime object returned
# from the database to string. See below.
document_query = go(self.database.list, {})
request = self.server.receives()
# returns to pymongo a list containing only 1 the document
request.reply([DOCUMENT])
self.assertIsInstance(document_query()[0]['request_time'], str)
self.assertEqual(
document['request_time'].strftime("%Y-%m-%dT%H:%M:%S"),
document_query()[0]['request_time']
)
# database.py
class MongoDatabase(object):
def __init__(self, uri):
self.client = MongoClient(uri)
self.db = self.client.db
self.collection = self.db.collection
def list(self, query):
data_list = list()
for data in self.collection.find(query):
data['id'] = str(data.pop('_id'))
data = self._convert_datetime_to_str(data)
data_list.append(data)
return data_list
def _convert_datetime_to_str(self, data):
for key in data.keys():
if isinstance(data[key], datetime.datetime):
data[key] = data[key].strftime('%Y-%m-%dT%H:%M:%S')
return data
MockupDB was the first solution we came across when looking for alternatives to unit testing pymongo methods, subsequently we also found a few other packages mocking MongoDB – Mongomock and Ming (with its mongo in memory module), although we have not had a chance to look into these as yet.
Generally, MockupDB is a powerful yet flexible package which can be used for both simple and more complex testing cases providing simple functions with a very small learning curve. So far we have only used it for simple unit tests but we are also keen to try out more complex scenarios with e.g. multiple MongoDB configurations where there are network failures.
If you are interested in more information about MockupDB and testing examples you can check these links:
- Black Pipe Testing series – an interesting testing concept evolved from classical black box testing with single input and output to entities which have multiple inputs and outputs (eg northbound and southbound interfaces)
- Example of MockupDB tests
Hat tip to Joe Drumgoole for pointing us in the right direction and to Jesse Jiryu Davis who did the hard work of writing MockupDB!