【问题标题】:Django ORM - mock values().filter() chainDjango ORM - 模拟 values().filter() 链
【发布时间】:2012-10-04 17:38:48
【问题描述】:

我试图模拟 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_nosemocktest.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 包含我作为返回值添加到 QuerySetMock 的 MyModel 实例:

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

我模拟了filter() 方法而不是values() 方法,因为我发现它将是最后一次评估的调用,尽管我不确定。

测试将失败,因为第二个结果变量不包含任何 MyModel 实例。

为了确保filter() 方法真的被嘲笑,我添加了一个“调试打印”语句:

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

返回:

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

我做错了什么?

【问题讨论】:

    标签: python django django-orm python-mock django-nose


    【解决方案1】:

    试试这个:

    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)
    

    【讨论】:

    • 不应该是'values')吗?
    【解决方案2】:

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

    此外,修补属性的顺序很重要,并且必须与您在测试函数中调用它们的顺序相匹配。

    【讨论】:

    • 这很有帮助,我只需要修改它以匹配我的 orm 查询 - mock.filter.return_value.first.return_value
    • 它为我返回 而不是实际值。
    猜你喜欢
    • 2015-10-17
    • 1970-01-01
    • 2022-12-01
    • 1970-01-01
    • 2018-06-13
    • 2014-04-07
    • 1970-01-01
    • 2017-10-11
    • 2020-03-21
    相关资源
    最近更新 更多