Skip to content

Commit 585532d

Browse files
committed
Remove mongomock:// support, update tests and improve doc to support the new 'mongo_client_class' parameter of connect()
1 parent ee5b1bb commit 585532d

File tree

5 files changed

+101
-64
lines changed

5 files changed

+101
-64
lines changed

docs/changelog.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ Changelog
77
Development
88
===========
99
- (Fill this out as you fix issues and develop your features).
10+
- Added `mongo_client_class` optional parameter to connect() to allow to use an alternative mongo client than pymongo.MongoClient.
11+
Typically to support mock mongo libraries like mongomock, montydb, mongita #2729
12+
- BREAKING CHANGE: connecting MongoEngine with mongomock should now use the new `mongo_client_class`
13+
For more info, check https://docs.mongoengine.org/guide/mongomock.html
1014

1115
Changes in 0.26.0
1216
=================

docs/guide/mongomock.rst

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,28 @@
1-
==============================
1+
=========================
22
Use mongomock for testing
3-
==============================
3+
=========================
44

5-
`mongomock <https://github.com/vmalloc/mongomock/>`_ is a package to do just
6-
what the name implies, mocking a mongo database.
5+
Although we recommend running your tests against a regular MongoDB server, it is sometimes useful to plug
6+
MongoEngine to alternative implementations (mongomock, montydb, mongita, etc).
7+
8+
`mongomock <https://github.com/vmalloc/mongomock/>`_ is historically the one suggested for MongoEngine and is
9+
a package to do just what the name implies, mocking a mongo database.
710

811
To use with mongoengine, simply specify mongomock when connecting with
912
mongoengine:
1013

1114
.. code-block:: python
1215
13-
connect('mongoenginetest', host='mongomock://localhost')
16+
import mongomock
17+
18+
connect('mongoenginetest', host='mongodb://localhost', mongo_client_class=mongomock.MongoClient)
1419
conn = get_connection()
1520
1621
or with an alias:
1722

1823
.. code-block:: python
1924
20-
connect('mongoenginetest', host='mongomock://localhost', alias='testdb')
25+
connect('mongoenginetest', host='mongodb://localhost', mongo_client_class=mongomock.MongoClient, alias='testdb')
2126
conn = get_connection('testdb')
2227
2328
Example of test file:
@@ -34,7 +39,7 @@ Example of test file:
3439
3540
@classmethod
3641
def setUpClass(cls):
37-
connect('mongoenginetest', host='mongomock://localhost')
42+
connect('mongoenginetest', host='mongodb://localhost', mongo_client_class=mongomock.MongoClient)
3843
3944
@classmethod
4045
def tearDownClass(cls):

mongoengine/connection.py

Lines changed: 18 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,8 @@ def _get_connection_settings(
7474
:param authentication_mechanism: database authentication mechanisms.
7575
By default, use SCRAM-SHA-1 with MongoDB 3.0 and later,
7676
MONGODB-CR (MongoDB Challenge Response protocol) for older servers.
77-
:param is_mock: explicitly use mongomock for this connection
78-
(can also be done by using `mongomock: // ` as db host prefix)
79-
:param connection_class: using alternative connection client other than
80-
pymongo or mongomock, e.g. montydb, that provides pymongo alike
77+
:param mongo_client_class: using alternative connection client other than
78+
pymongo.MongoClient, e.g. mongomock, montydb, that provides pymongo alike
8179
interface but not necessarily for connecting to a real mongo instance.
8280
:param kwargs: ad-hoc parameters to be passed into the pymongo driver,
8381
for example maxpoolsize, tz_aware, etc. See the documentation
@@ -105,22 +103,17 @@ def _get_connection_settings(
105103
resolved_hosts = []
106104
for entity in conn_host:
107105

108-
# Handle Mongomock
109-
if entity.startswith("mongomock://"):
110-
conn_settings["is_mock"] = True
111-
# `mongomock://` is not a valid url prefix and must be replaced by `mongodb://`
112-
new_entity = entity.replace("mongomock://", "mongodb://", 1)
113-
resolved_hosts.append(new_entity)
114-
115-
uri_dict = uri_parser.parse_uri(new_entity)
116-
117-
database = uri_dict.get("database")
118-
if database:
119-
conn_settings["name"] = database
106+
# Reject old mongomock integration
107+
# To be removed in a few versions after 0.27.0
108+
if entity.startswith("mongomock://") or kwargs.get("is_mock"):
109+
raise Exception(
110+
"Use of mongomock:// URI or 'is_mock' were removed in favor of 'mongo_client_class=mongomock.MongoClient'. "
111+
"Check the CHANGELOG for more info"
112+
)
120113

121114
# Handle URI style connections, only updating connection params which
122115
# were explicitly specified in the URI.
123-
elif "://" in entity:
116+
if "://" in entity:
124117
uri_dict = uri_parser.parse_uri(entity)
125118
resolved_hosts.append(entity)
126119

@@ -222,10 +215,8 @@ def register_connection(
222215
:param authentication_mechanism: database authentication mechanisms.
223216
By default, use SCRAM-SHA-1 with MongoDB 3.0 and later,
224217
MONGODB-CR (MongoDB Challenge Response protocol) for older servers.
225-
:param is_mock: explicitly use mongomock for this connection
226-
(can also be done by using `mongomock: // ` as db host prefix)
227-
:param connection_class: using alternative connection client other than
228-
pymongo or mongomock, e.g. montydb, that provides pymongo alike
218+
:param mongo_client_class: using alternative connection client other than
219+
pymongo.MongoClient, e.g. mongomock, montydb, that provides pymongo alike
229220
interface but not necessarily for connecting to a real mongo instance.
230221
:param kwargs: ad-hoc parameters to be passed into the pymongo driver,
231222
for example maxpoolsize, tz_aware, etc. See the documentation
@@ -332,39 +323,30 @@ def _clean_settings(settings_dict):
332323
conn_settings = _clean_settings(raw_conn_settings)
333324

334325
# Determine if we should use PyMongo's or mongomock's MongoClient.
335-
is_mock = conn_settings.pop("is_mock", False)
336-
if is_mock:
337-
try:
338-
import mongomock
339-
except ImportError:
340-
raise RuntimeError("You need mongomock installed to mock MongoEngine.")
341-
connection_class = mongomock.MongoClient
342-
343-
elif "connection_class" in conn_settings:
344-
connection_class = conn_settings.pop("connection_class")
345-
326+
if "mongo_client_class" in conn_settings:
327+
mongo_client_class = conn_settings.pop("mongo_client_class")
346328
else:
347-
connection_class = MongoClient
329+
mongo_client_class = MongoClient
348330

349331
# Re-use existing connection if one is suitable.
350332
existing_connection = _find_existing_connection(raw_conn_settings)
351333
if existing_connection:
352334
connection = existing_connection
353335
else:
354336
connection = _create_connection(
355-
alias=alias, connection_class=connection_class, **conn_settings
337+
alias=alias, mongo_client_class=mongo_client_class, **conn_settings
356338
)
357339
_connections[alias] = connection
358340
return _connections[alias]
359341

360342

361-
def _create_connection(alias, connection_class, **connection_settings):
343+
def _create_connection(alias, mongo_client_class, **connection_settings):
362344
"""
363345
Create the new connection for this alias. Raise
364346
ConnectionFailure if it can't be established.
365347
"""
366348
try:
367-
return connection_class(**connection_settings)
349+
return mongo_client_class(**connection_settings)
368350
except Exception as e:
369351
raise ConnectionFailure(f"Cannot connect to database {alias} :\n{e}")
370352

tests/test_connection.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,21 @@ def test_connect(self):
5353
connect("mongoenginetest")
5454

5555
conn = get_connection()
56-
assert isinstance(conn, pymongo.mongo_client.MongoClient)
56+
assert isinstance(conn, pymongo.MongoClient)
5757

5858
db = get_db()
5959
assert isinstance(db, pymongo.database.Database)
6060
assert db.name == "mongoenginetest"
6161

6262
connect("mongoenginetest2", alias="testdb")
6363
conn = get_connection("testdb")
64-
assert isinstance(conn, pymongo.mongo_client.MongoClient)
64+
assert isinstance(conn, pymongo.MongoClient)
65+
66+
connect(
67+
"mongoenginetest2", alias="testdb3", mongo_client_class=pymongo.MongoClient
68+
)
69+
conn = get_connection("testdb")
70+
assert isinstance(conn, pymongo.MongoClient)
6571

6672
def test_connect_disconnect_works_properly(self):
6773
class History1(Document):

tests/test_connection_mongomock.py

Lines changed: 59 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,45 +32,76 @@ def tearDown(self):
3232
mongoengine.connection._connections = {}
3333
mongoengine.connection._dbs = {}
3434

35+
@require_mongomock
36+
def test_connect_raise_if_mongomock_uri_provided(self):
37+
with pytest.raises(
38+
Exception, match="Use of mongomock:// URI or 'is_mock' were removed"
39+
):
40+
connect("test", host="mongomock://localhost")
41+
42+
@require_mongomock
43+
def test_connect_raise_if_is_mock_provided(self):
44+
with pytest.raises(
45+
Exception, match="Use of mongomock:// URI or 'is_mock' were removed"
46+
):
47+
connect("test", host="mongodb://localhost", is_mock=True)
48+
3549
@require_mongomock
3650
def test_connect_in_mocking(self):
3751
"""Ensure that the connect() method works properly in mocking."""
38-
connect("mongoenginetest", host="mongomock://localhost")
52+
connect(
53+
"mongoenginetest",
54+
host="mongodb://localhost",
55+
mongo_client_class=mongomock.MongoClient,
56+
)
3957
conn = get_connection()
4058
assert isinstance(conn, mongomock.MongoClient)
4159

42-
connect("mongoenginetest2", host="mongomock://localhost", alias="testdb2")
60+
connect(
61+
"mongoenginetest2",
62+
host="mongodb://localhost",
63+
mongo_client_class=mongomock.MongoClient,
64+
alias="testdb2",
65+
)
4366
conn = get_connection("testdb2")
4467
assert isinstance(conn, mongomock.MongoClient)
4568

4669
connect(
4770
"mongoenginetest3",
4871
host="mongodb://localhost",
49-
is_mock=True,
72+
mongo_client_class=mongomock.MongoClient,
5073
alias="testdb3",
5174
)
5275
conn = get_connection("testdb3")
5376
assert isinstance(conn, mongomock.MongoClient)
5477

55-
connect("mongoenginetest4", is_mock=True, alias="testdb4")
78+
connect(
79+
"mongoenginetest4",
80+
mongo_client_class=mongomock.MongoClient,
81+
alias="testdb4",
82+
)
5683
conn = get_connection("testdb4")
5784
assert isinstance(conn, mongomock.MongoClient)
5885

5986
connect(
6087
host="mongodb://localhost:27017/mongoenginetest5",
61-
is_mock=True,
88+
mongo_client_class=mongomock.MongoClient,
6289
alias="testdb5",
6390
)
6491
conn = get_connection("testdb5")
6592
assert isinstance(conn, mongomock.MongoClient)
6693

67-
connect(host="mongomock://localhost:27017/mongoenginetest6", alias="testdb6")
94+
connect(
95+
host="mongodb://localhost:27017/mongoenginetest6",
96+
mongo_client_class=mongomock.MongoClient,
97+
alias="testdb6",
98+
)
6899
conn = get_connection("testdb6")
69100
assert isinstance(conn, mongomock.MongoClient)
70101

71102
connect(
72-
host="mongomock://localhost:27017/mongoenginetest7",
73-
is_mock=True,
103+
host="mongodb://localhost:27017/mongoenginetest7",
104+
mongo_client_class=mongomock.MongoClient,
74105
alias="testdb7",
75106
)
76107
conn = get_connection("testdb7")
@@ -84,7 +115,10 @@ def test_default_database_with_mocking(self):
84115
class SomeDocument(Document):
85116
pass
86117

87-
conn = connect(host="mongomock://localhost:27017/mongoenginetest")
118+
conn = connect(
119+
host="mongodb://localhost:27017/mongoenginetest",
120+
mongo_client_class=mongomock.MongoClient,
121+
)
88122
some_document = SomeDocument()
89123
# database won't exist until we save a document
90124
some_document.save()
@@ -96,7 +130,10 @@ class SomeDocument(Document):
96130
def test_basic_queries_against_mongomock(self):
97131
disconnect_all()
98132

99-
connect(host="mongomock://localhost:27017/mongoenginetest")
133+
connect(
134+
host="mongodb://localhost:27017/mongoenginetest",
135+
mongo_client_class=mongomock.MongoClient,
136+
)
100137

101138
class Person(Document):
102139
name = StringField()
@@ -129,35 +166,38 @@ def test_connect_with_host_list(self):
129166
130167
Uses mongomock to test w/o needing multiple mongod/mongos processes
131168
"""
132-
connect(host=["mongomock://localhost"])
169+
connect(host=["mongodb://localhost"], mongo_client_class=mongomock.MongoClient)
133170
conn = get_connection()
134171
assert isinstance(conn, mongomock.MongoClient)
135172

136-
connect(host=["mongodb://localhost"], is_mock=True, alias="testdb2")
137-
conn = get_connection("testdb2")
138-
assert isinstance(conn, mongomock.MongoClient)
139-
140-
connect(host=["localhost"], is_mock=True, alias="testdb3")
173+
connect(
174+
host=["localhost"],
175+
mongo_client_class=mongomock.MongoClient,
176+
alias="testdb3",
177+
)
141178
conn = get_connection("testdb3")
142179
assert isinstance(conn, mongomock.MongoClient)
143180

144181
connect(
145-
host=["mongomock://localhost:27017", "mongomock://localhost:27018"],
182+
host=["mongodb://localhost:27017", "mongodb://localhost:27018"],
146183
alias="testdb4",
184+
mongo_client_class=mongomock.MongoClient,
147185
)
148186
conn = get_connection("testdb4")
149187
assert isinstance(conn, mongomock.MongoClient)
150188

151189
connect(
152190
host=["mongodb://localhost:27017", "mongodb://localhost:27018"],
153-
is_mock=True,
191+
mongo_client_class=mongomock.MongoClient,
154192
alias="testdb5",
155193
)
156194
conn = get_connection("testdb5")
157195
assert isinstance(conn, mongomock.MongoClient)
158196

159197
connect(
160-
host=["localhost:27017", "localhost:27018"], is_mock=True, alias="testdb6"
198+
host=["localhost:27017", "localhost:27018"],
199+
mongo_client_class=mongomock.MongoClient,
200+
alias="testdb6",
161201
)
162202
conn = get_connection("testdb6")
163203
assert isinstance(conn, mongomock.MongoClient)

0 commit comments

Comments
 (0)