Вымывание 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 не заставит вас реализовать свою полную логику.