Closed Itay4 closed 2 years ago
Cab you provide complete example to investigate?
here you go:
source (named s.py
):
from motor.motor_asyncio import AsyncIOMotorDatabase
async def upsert(db: AsyncIOMotorDatabase):
result = await db["test-collection"].update_one(
filter={"_id": "id"},
update={
"$set": {"field": "value"},
},
upsert=True,
)
if result.acknowledged is False:
raise Exception()
test:
from pymongo.results import UpdateResult
import pytest
from mongomock_motor import AsyncMongoMockClient
from s import upsert
@pytest.mark.asyncio
async def test_upsert(mocker):
db = AsyncMongoMockClient()["test-db"]
mocker.patch.object(db["test-collection"], "update_one", return_value=UpdateResult({}, False))
with pytest.raises(Exception):
await upsert(db)
this test passes on v0.0.8 and fails on v0.0.9 with the following error:
@pytest.mark.asyncio
async def test_example(mocker):
db = AsyncMongoMockClient()["test-db"]
mocker.patch.object(db["test-collection"], "update_one", return_value=UpdateResult({}, False))
> with pytest.raises(Exception):
E Failed: DID NOT RAISE <class 'Exception'>
because the condition is not met (for acknowledged
) and an exception is not raised
I looked into issue. Yeah, it was caused by changes in #13, but it's not that simple. You mocked property of the object that was returned from db["test-collection"]
but use other object in the function. It's different objects.
>>> import pymongo
>>> client = pymongo.MongoClient(addr)
>>> database = client['database']
>>> database['collection'] == database['collection']
True
>>> database['collection'] is database['collection']
False
>>>
If you can pass collection to upsert
– you can just patch.object
that collection and pass it as argument. If you really need to use database object, it will require some changes to test code as well as to mongomock_motor.
Sample of "clean" solution:
from unittest.mock import patch, AsyncMock
from bson import ObjectId
from pymongo.results import UpdateResult
import pytest
from mongomock_motor import AsyncMongoMockClient
async def sample_function(collection):
result = await collection.update_one(
filter={"_id": ObjectId()},
update={"$set": {"field": "value"}},
upsert=True,
)
if result.acknowledged is False:
raise RuntimeError()
@pytest.mark.asyncio
async def test_upsert():
collection = AsyncMongoMockClient()['test']['test']
with patch.object(collection, 'update_one', AsyncMock(return_value=UpdateResult({}, False))):
with pytest.raises(RuntimeError):
await sample_function(collection)
I've implemented possible improvement to issue in MR #15, you can check it out
looks good, thanks for looking into it !
i believe there is a regression in version 0.0.9 which causes our mocks in the tests to fail on the following:
in our tests, we are mocking
update_one
to returnUpdateResult({}, False)
in order to mock anupsert
failure - meaningresult.acknowledged
is set toFalse
this worked fine with previous versions, but breaks on 0.0.9
i suspect its related to the changes introduced in https://github.com/michaelkryukov/mongomock_motor/pull/13
can you please look into it?