I am trying to mock a chained call on the Djangos model.Manager()
class. For now I want to mock the values()
and filter()
method.
To test that I created a little test project:
pip install django mock mock-django nose django-nose
django-admin.py startproject mocktest
manage.py startapp mockme
django_nose
and mocktest.mockme
to INSTALLED_APPS
(settings.py)TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
to settings.pyTo verfiy that everything is setup correctly I ran manage.py test
. One test is run, the standard test Django creates when you create an app.
Next thing I did was to create a very simple model.
mockme/models.py
from django.db import models
class MyModel(models.Model):
name = models.CharField(max_length=50)
Next thing I did was to create a simple function that uses MyModel
. That's the function I want to test later.
mockme/functions.py
from models import MyModel
def chained_query():
return MyModel.objects.values('name').filter(name='Frank')
Nothing special is happening here. The function is filtering the MyModel objects to find all instances where name='Frank'
. The call to values() will return a ValuesQuerySet
which will only contain the name field of all found MyModel instances.
mockme/tests.py
import mock
from django.test import TestCase
from mocktest.mockme.models import MyModel
from mocktest.mockme.functions import chained_query
from mock_django.query import QuerySetMock
class SimpleTest(TestCase):
def test_chained_query(self):
# without mocked queryset the result should be 0
result = chained_query()
self.assertEquals(result.count(), 0)
# now try to mock values().filter() and reeturn
# one 'Frank'.
qsm = QuerySetMock(MyModel, MyModel(name='Frank'))
with mock.patch('django.db.models.Manager.filter', qsm):
result = chained_query()
self.assertEquals(result.count(), 1)
The first assertEquals will evaluate as successful. No instances are returned since the model Manager is not mocked yet. When the second assertEquals is called I expect result
to contain the MyModel instance I added as return value to the QuerySetMock:
qsm = QuerySetMock(MyModel, MyModel(name='Frank'))
I mocked the filter()
method and not the values()
method since I found it'll be the last evaluated call, though I am not sure.
The test will fail because the second result variable won't contain any MyModel instances.
To be sure that the filter()
method is really mocked I added a "debug print" statement:
from django.db import models
print models.Manager.filter
which returned:
<SharedMock name='mock.iterator' id='4514208912'>
What am I doing wrong?
Try this:
import mock
from mocktest.mockme.models import MyModel
class SimpleTest(TestCase):
def test_chained_query(self):
my_model_value_mock = mock.patch(MyModel.objects, 'value')
my_model_value_mock.return_value.filter.return_value.count.return_value = 10000
self.assertTrue(my_model_value_mock.return_value.filter.return_value.count.called)
@Gin's answer got me most of the way there, but in my case I'm patching MyModel.objects
, and the query I'm mocking looks like this:
MyModel.objects.filter(arg1=user, arg2=something_else).order_by('-something').first()
so this worked for me:
@patch('MyModel.objects')
def test_a_function(mock, a_fixture):
mock.filter.return_value.order_by.return_value.first.return_value = a_fixture
result = the_func_im_testing(arg1, arg2)
assert result == 'value'
Also, the order of the patched attributes matters, and must match the order you're calling them within the tested function.
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