mock-alchemy
mock-alchemy copied to clipboard
db.query(User).filter(User.id = id).first() always return None
Describe the bug
It's fine if sfwafawfcwafawf
but first
:
db.query(User).filter(User.id = id).sfwafawfcwafawf()= 112
but first
:
db.query(User).filter(User.id = id).first() = None
To Reproduce For example:
def test_find_user():
db = UnifiedAlchemyMagicMock()
db.query.return_value.filter.return_value.first.return_value = 112
UserRepository(db).find_user(id=1)
assert 0
and
def find_user(id: int) -> None:
print("result of mock db:", db.query(User).filter(User.id = id).first())
Expected behavior
If i set: db.query.return_value.filter.return_value.first.return_value = 112
So the result of db.query().filter().first()
should be 112
Screenshots
If applicable, add screenshots to help explain your problem.
- OS: MacOS Monterey
- Version 12.3.1
I have the same problem! OS: Kubuntu 22.04 Version: mock-alchemy==0.2.5
I think I found the error but I am not sure if it is this one:
- mock_alchemy/mocking.py in line 620:
for calls, result in sorted_mock_data:
calls = [
sqlalchemy_call(
i,
with_name=True,
base_call=self.unify.get(i[0]) or Call,
)
for i in calls
]
if all(c in previous_calls for c in calls): # line 620
return self.boundary[_mock_name](result, *args, **kwargs)
This check if the some call exists in previous_calls
(created in line 590)
For example, in my case I have this:
- Calls: [call.query(User), call.filter(User.username == "test"), call.first()]
- Return value: [User(id=1, username="test", password="test")]
Code:
session = UnifiedAlchemyMock(
data=[([call.query(User), call.filter(User.username == "test"), call.first()], [User(id=1, username="test", password="test")])]
)
When line 620 is to be executed, the previous_calls has this:
[
call.filter(BinaryExpression(sql='users.username = :username_1', params={'username_1': 'test'})),
call.query(<class 'project.models.users.User'>),
call.__len__(),
call.__str__(),
call.__len__()
]
The call first()
is not added in previous_calls
, then it fails.
I guess it must be because the first() is not a method of sqlalchemy as such, i.e. as a query, but it is a method of the same object that returns the filter and returns the first of the list as if it were a slicing (any_list[0])
# sqlalchemy/orm/query.py - line 2799
def first(self):
if self._statement is not None:
return self._iter().first()
else:
return self.limit(1)._iter().first()