Насмешка над набором запросов Django для тестирования функции, которая принимает набор запросов - PullRequest
30 голосов
/ 09 сентября 2011

У меня есть служебная функция в моем проекте Django, она берет набор запросов, получает некоторые данные из него и возвращает результат.Я хотел бы написать несколько тестов для этой функции.Есть ли в любом случае «издеваться» над QuerySet?Я хотел бы создать объект, который не касается базы данных, и я могу предоставить ему список значений для использования (например, некоторые поддельные строки), а затем он будет действовать как набор запросов и позволит кому-тосделать поиск полей на нем / фильтр / получить / все и т. д.

Уже существует что-то подобное?

Ответы [ 9 ]

15 голосов
/ 09 сентября 2011

Конечно, вы можете издеваться над QuerySet, вы можете издеваться над чем угодно.

Вы можете создать объект самостоятельно, предоставить ему необходимый интерфейс и заставить его возвращать любые данные, которые вам нравятся. В глубине души насмешка - это не что иное, как создание «двойника теста», который достаточно эффективен для целей ваших тестов.

Низкотехнологичный способ начать - определить объект:

class MockQuerySet(object):
    pass

затем создайте один из них и передайте его на тест. Тест не пройден, скорее всего, на AttributeError. Это скажет вам, что вам нужно реализовать на вашем MockQuerySet. Повторяйте, пока ваш объект не станет достаточно богатым для ваших тестов.

12 голосов
/ 10 сентября 2012

У меня та же проблема, и похоже, что какой-то приятный человек написал библиотеку для насмешливых QuerySets, она называется mock-django , а конкретный код, который вам понадобится, находится здесь https://github.com/dcramer/mock-django/blob/master/mock_django/query.py Я думаю, что тогда вы можете просто залатать функцию объектов вашей модели, чтобы вернуть один из этих объектов QuerySetMock, которые вы настроили для возврата чего-то ожидаемого!

7 голосов
/ 16 марта 2016

Для пустого набора запросов я бы просто использовал 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 и т. Д.

4 голосов
/ 14 января 2015

Для этого я использую функцию 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``.
1 голос
/ 04 апреля 2019

Вы смотрели на 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/

0 голосов
/ 04 апреля 2019

Вы можете издеваться так:

@patch('django.db.models.query.QuerySet')
def test_returning_distinct_records_for_city(self, mock_qs):
    self.assertTrue(mock_qs.called)
0 голосов
/ 12 мая 2018

Попробуйте django_mock_queries библиотеку , которая позволяет смоделировать доступ к базе данных и при этом использовать некоторые функции набора запросов Django, такие как фильтрация.

Полное раскрытие: я добавил некоторые функции в проект.

0 голосов
/ 09 сентября 2011

Одним из первых советов было бы разделить функцию на две части, одна из которых создает набор запросов. и тот, который управляет выходом этого. Таким образом, тестирование второй части является простым.

Для проблемы с базой данных я исследовал, использует ли django sqlite-in-memory, и выяснил, что В последней версии django используется база данных sqlite -in-memory, начиная с Страница юнит-теста django :

При использовании ядра базы данных SQLite в тестах по умолчанию используется база данных в памяти (то есть база данных будет создана в памяти, полностью обходя файловую систему!).

Насмешка над объектом QuerySet не заставит вас использовать его полную логику.

0 голосов
/ 09 сентября 2011

Не то, что я знаю, но почему бы не использовать реальный набор запросов? Инфраструктура тестирования полностью настроена для того, чтобы вы могли создавать образцы данных в своем тесте, и база данных создается заново при каждом тесте, поэтому, похоже, нет никаких причин не использовать реальную вещь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...