I have the following function:
import unittest
from unittest import mock
def get_payments(order):
return order.payments.filter(status='complete').order_by('-date_added)
I want to mock the filter
method and the order_by
to check the arguments with which are called.
I tried:
class TestPayments(unittest.TestCase):
@mock.patch('path.Order.payments.filter.order_by')
@mock.patch('path.Order.payments.filter')
def test_get_payments(self, mock1, mock2):
mock1.assert_called_with(status='complete')
mock2.assert_called_with('-date_added')
Another mock I tried:
@mock.patch('path.Payment.objects.filter.order_by')
@mock.patch('path.Payment.objects.filter')
@mock.patch('path.Order.payments.objects.filter.order_by')
@mock.patch('path.Order.payments.objects.filter')
In last two mocks I have an error that path.Order
does not exists.
I already used a direct mock for a query like Payment.objects.filter()
and is working, but starting from a related model like Order
I failed.
The relationship between Order
and Payment
is how you would expect, one to many.
by mocking objects I resolved this.
order = MagicMock(side_effect=Order())
order.payments.filter.return_value = MagicMock(side_effect=Payment.objects.filter(id=0))
order.payments.filter.return_value.order_by.return_value = [Payment()]
order.payments.filter.assert_called_with(status='complete')
order.payments.filter.return_value.order_by.assert_called_with('-date_updated')
To explain what happens here: QuerySet methods like filter()
, exclude()
, order_by()
etc. return a QuerySet, that is why they can be chained.
What you tried first is trying to patch the methods as if they would be in a package hierarchy. What you ended up doing is not patching the method but mocking the return value for each chained method, which is how it is done.
Unfortunately there is not much documentation on this. When I ran into this problem I had some stackoverflow answers that helped me, but I can't seem to find them again.
Similar questions (where the answers don't really provide an explaination):
Django ORM - mock values().filter() chain
Mocking a Django Queryset in order to test a function that takes a queryset
There are libraries out there that might help you: mock-django provides an ORM mocking class. Their approach to mocking QuerySet methods is quite interesting. What I personally found very useful for testing Django models is Model Mommy, because it helps you to create easy mocks of models.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With