-20đź‘Ť
Not that I know of, but why not use an actual queryset? The test framework is all set up to allow you to create sample data within your test, and the database is re-created on every test, so there doesn’t seem to be any reason not to use the real thing.
17đź‘Ť
For an empty Queryset, I’d go simply for using none
as keithhackbarth has already stated.
However, to mock a Queryset that will return a list of values, I prefer to use a Mock with a spec
of the Model’s manager. As an example (Python 2.7 style – I’ve used the external Mock library), here’s a simple test where the Queryset is filtered and then counted:
from django.test import TestCase
from mock import Mock
from .models import Example
def queryset_func(queryset, filter_value):
"""
An example function to be tested
"""
return queryset.filter(stuff=filter_value).count()
class TestQuerysetFunc(TestCase):
def test_happy(self):
"""
`queryset_func` filters provided queryset and counts result
"""
m_queryset = Mock(spec=Example.objects)
m_queryset.filter.return_value = m_queryset
m_queryset.count.return_value = 97
result = func_to_test(m_queryset, '__TEST_VALUE__')
self.assertEqual(result, 97)
m_queryset.filter.assert_called_once_with(stuff='__TEST_VALUE__')
m_queryset.count.assert_called_once_with()
However, to fulfil the question, instead of setting a return_value
for count
, this could easily be adjusted to be a list
of model instances returned from all
.
Note that chaining is handled by setting the filter
to return the mocked queryset:
m_queryset.filter.return_value = m_queryset
This would need to be applied for any queryset methods used in the function under test, e.g. exclude
, etc.
- [Django]-How do you skip a unit test in Django?
- [Django]-Model.objects.get() or None
- [Django]-What's the best way to handle Django's objects.get?
15đź‘Ť
Of course you can mock a QuerySet, you can mock anything.
You can create an object yourself, and give it the interface you need, and have it return any data you like. At heart, mocking is nothing more than providing a “test double” that acts enough like the real thing for your tests’ purposes.
The low-tech way to get started is to define an object:
class MockQuerySet(object):
pass
then create one of these, and hand it to your test. The test will fail, likely on an AttributeError
. That will tell you what you need to implement on your MockQuerySet
. Repeat until your object is rich enough for your tests.
- [Django]-Django: guidelines for speeding up template rendering performance
- [Django]-Create Django model or update if exists
- [Django]-Connect to a DB using psycopg2 without password
14đź‘Ť
I am having the same issue, and it looks like some nice person has written a library for mocking QuerySets, it is called mock-django and the specific code you will need is here https://github.com/dcramer/mock-django/blob/master/mock_django/query.py I think you can then just patch you models objects function to return one of these QuerySetMock objects that you have set up to return something expected!
- [Django]-How to select_related when using .get() in django?
- [Django]-How to get the current language in Django?
- [Django]-Django 1.9 deprecation warnings app_label
5đź‘Ť
For this I use Django’s .none() function.
For example:
class Location(models.Model):
name = models.CharField(max_length=100)
mock_locations = Location.objects.none()
This is the method used frequently in Django’s own internal test cases. Based on comments in the code
Calling none() will create a queryset that never returns any objects and no
+query will be executed when accessing the results. A qs.none() queryset
+is an instance of ``EmptyQuerySet``.
- [Django]-Database returned an invalid value in QuerySet.dates()
- [Django]-Django REST Framework : "This field is required." with required=False and unique_together
- [Django]-How to recursively query in django efficiently?
2đź‘Ť
Try out the django_mock_queries
library that lets you mock out the database access, and still use some of the Django query set features like filtering.
Full disclosure: I contributed some features to the project.
- [Django]-Using Django Managers vs. staticmethod on Model class directly
- [Django]-Django project base template
- [Django]-Django equivalent of PHP's form value array/associative array
2đź‘Ť
Have you looked into FactoryBoy? https://factoryboy.readthedocs.io/en/latest/orms.html
It’s a fixtures replacement tool with support for the django orm – factories basically generate orm-like objects (either in memory or in a test database).
Here’s a great article for getting started: https://www.caktusgroup.com/blog/2013/07/17/factory-boy-alternative-django-testing-fixtures/
- [Django]-CommandError: You must set settings.ALLOWED_HOSTS if DEBUG is False
- [Django]-How can I test https connections with Django as easily as I can non-https connections using 'runserver'?
- [Django]-Django form – set label
0đź‘Ť
One first advice would be to split the function in two parts, one that creates the queryset
and one that manipulates the output of it. In this way testing the second part is straightforward.
For the database problem, I investigated if django uses sqlite-in-memory and I found out that
recent version of django uses the sqlite -in-memory database, from The django unittest page:
When using the SQLite database engine the tests will by default use an
in-memory database (i.e., the database will be created in memory,
bypassing the filesystem entirely!).
Mocking the QuerySet object will not make you exercise its full logic.
- [Django]-Django set default form values
- [Django]-How to register users in Django REST framework?
- [Django]-Django 1.8 migrate is not creating tables
0đź‘Ť
You can mock like this:
@patch('django.db.models.query.QuerySet')
def test_returning_distinct_records_for_city(self, mock_qs):
self.assertTrue(mock_qs.called)
- [Django]-How to recreate a deleted table with Django Migrations?
- [Django]-Django setUpTestData() vs. setUp()
- [Django]-Django: Get list of model fields?