Вымывание Django Queryset для проверки функции, которая принимает запрос
У меня есть функция утилиты в моем проекте Django, она принимает запрос, получает некоторые данные из него и возвращает результат. Я хотел бы написать несколько тестов для этой функции. Нужно ли вообще "издеваться" над QuerySet? Я хотел бы создать объект, который не касается базы данных, и я могу предоставить ему список используемых значений (т.е. Некоторые поддельные строки), а затем он будет действовать точно так же, как набор запросов, и позволит кому-то поиск полей на нем /filter/get/all и т.д.
Что-то вроде этого уже существует?
Ответы
Ответ 1
Не то, чтобы я знал, но почему бы не использовать фактический набор запросов? Рамка тестирования настроена так, чтобы вы могли создавать образцы данных в своем тесте, и база данных воссоздается в каждом тесте, поэтому нет никакой причины не использовать настоящую вещь.
Ответ 2
Конечно, вы можете издеваться над QuerySet, вы можете издеваться над чем угодно.
Вы можете создать объект самостоятельно, предоставить ему необходимый интерфейс и заставить его возвращать любые данные, которые вам нравятся. В глубине души издевательство - это не что иное, как создание "двойника теста", который действует как реальная вещь для целей ваших тестов.
Низкотехнологичный способ начать - определить объект:
class MockQuerySet(object):
pass
затем создайте один из них и передайте его на проверку. Тест не пройден, вероятно, при AttributeError
. Это скажет вам, что вам нужно реализовать на вашем MockQuerySet
. Повторяйте, пока ваш объект не станет достаточно богатым для ваших тестов.
Ответ 3
У меня такая же проблема, и похоже, что какой-то хороший человек написал библиотеку для издевательств QuerySets, она называется mock-django и конкретный код, который вы вам понадобится https://github.com/dcramer/mock-django/blob/master/mock_django/query.py. Я думаю, вы можете просто исправить вашу функцию объектов объектов, чтобы вернуть один из этих объектов QuerySetMock, который вы настроили для возвращения чего-то ожидаемого!
Ответ 4
Для пустого Queryset я бы просто выбрал none
как уже сказал Keithhackbarth.
Однако, чтобы смоделировать Queryset, который будет возвращать список значений, я предпочитаю использовать Mock со spec
менеджера моделей. В качестве примера (стиль Python 2.7 - я использовал внешнюю библиотеку Mock), приведем простой тест, в котором Queryset фильтруется, а затем подсчитывается:
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()
Однако, чтобы ответить на этот вопрос, вместо установки return_value
для count
, его можно легко настроить, чтобы он представлял собой list
экземпляров модели, возвращаемых из all
.
Обратите внимание, что цепочка обрабатывается путем установки filter
для возврата смоделированного набора запросов:
m_queryset.filter.return_value = m_queryset
Это необходимо применять для любых методов набора запросов, используемых в тестируемой функции, например, exclude
и т.д.
Ответ 5
Для этого я использую функцию Django.none().
Например:
class Location(models.Model):
name = models.CharField(max_length=100)
mock_locations = Location.objects.none()
Это метод, который часто используется в собственных внутренних тестах Django. Основываясь на комментариях в коде
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``.
Ответ 6
Попробуйте библиотеку django_mock_queries
которая позволяет вам макетировать доступ к базе данных, и при этом использовать некоторые функции набора запросов Django, такие как фильтрация.
Полное раскрытие: я внес некоторые функции в проект.
Ответ 7
Вы можете издеваться так:
@patch('django.db.models.query.QuerySet')
def test_returning_distinct_records_for_city(self, mock_qs):
self.assertTrue(mock_qs.called)
Ответ 8
Вы смотрели в FactoryBoy? https://factoryboy.readthedocs.io/en/latest/orms.html Это инструмент для замены приспособлений с поддержкой django orm - фабрики в основном генерируют подобные объектам объекты (либо в памяти, либо в тестовой базе данных).
Вот отличная статья для начала работы: https://www.caktusgroup.com/blog/2013/07/17/factory-boy-alternative-django-testing-fixtures/
Ответ 9
Одним из первых советов было бы разделить функцию на две части: та, которая создает набор запросов
и тот, который манипулирует выходом. Таким образом, проверка второй части проста.
Для проблемы с базой данных я исследовал, использует ли django sqlite-in-memory, и я узнал, что
В последней версии django используется база данных sqlite -in-memory, от Страница unittest django:
При использовании механизма базы данных SQLite тесты по умолчанию будут использовать в-памяти (т.е. база данных будет создана в памяти, полностью обходя файловую систему!).
Откусывание объекта QuerySet не заставит вас реализовать свою полную логику.