Django ORM — фиктивные значения().filter() цепочки

Я пытаюсь издеваться над цепным вызовом класса Djangos model.Manager(). А пока я хочу поиздеваться над методом values() и filter().

Чтобы проверить это, я создал небольшой тестовый проект:

  1. Создайте виртуальную среду
  2. Беги pip install django mock mock-django nose django-nose
  3. Создать проект django-admin.py startproject mocktest
  4. Создать приложение manage.py startapp mockme
  5. Добавьте django_nose и mocktest.mockme в INSTALLED_APPS (settings.py)
  6. Добавьте TEST_RUNNER = 'django_nose.NoseTestSuiteRunner' в settings.py

Чтобы убедиться, что все настроено правильно, я запустил manage.py test. Запускается один тест, стандартный тест, который Django создает при создании приложения.

Следующее, что я сделал, это создал очень простую модель.

mockme/models.py

from django.db import models

class MyModel(models.Model):
    name = models.CharField(max_length=50)

Следующее, что я сделал, это создал простую функцию, использующую MyModel. Эту функцию я хочу протестировать позже.

mockme/functions.py

from models import MyModel

def chained_query():
    return MyModel.objects.values('name').filter(name='Frank')

Здесь ничего особенного не происходит. Функция фильтрует объекты MyModel, чтобы найти все экземпляры, где name='Frank'. Вызов values() вернет ValuesQuerySet, который будет содержать только поле имени всех найденных экземпляров MyModel.

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)

Первый assertEquals будет оценен как успешный. Экземпляры не возвращаются, так как диспетчер моделей еще не смоделирован. Когда вызывается второй assertEquals, я ожидаю, что result будет содержать экземпляр MyModel, который я добавил в качестве возвращаемого значения в QuerySetMock:

qsm = QuerySetMock(MyModel, MyModel(name='Frank'))

Я издевался над методом filter(), а не над методом values(), так как обнаружил, что это будет последний оцененный вызов, хотя я не уверен.

Тест завершится ошибкой, потому что вторая переменная результата не будет содержать никаких экземпляров MyModel.

Чтобы убедиться, что метод filter() действительно издевается, я добавил оператор "debug print":

from django.db import models
print models.Manager.filter

который вернулся:

<SharedMock name='mock.iterator' id='4514208912'>

Что я делаю не так?


person Jens    schedule 14.10.2012    source источник


Ответы (2)


Попробуй это:

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)
person Gin    schedule 25.07.2014
comment
Разве это не должно быть 'values')? - person Stefan Collier; 06.02.2018

Ответ @Gin помог мне в этом, но в моем случае я исправляю MyModel.objects, и запрос, над которым я издеваюсь, выглядит так:

MyModel.objects.filter(arg1=user, arg2=something_else).order_by('-something').first()

так что это сработало для меня:

@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'

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

person trpt4him    schedule 18.05.2019
comment
Это помогло, мне пришлось просто изменить его, чтобы он соответствовал моему запросу формы - mock.filter.return_value.first.return_value - person radtek; 21.05.2019
comment
он возвращает для меня ‹MagicMock name='objects.filter().first()' id='2020460232776'›, а не значение. - person DorZ; 09.09.2019