Django ORM - 模拟值()。filter()链

时间:2012-10-14 15:56:51

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

我试图在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
  7. 为了确保所有设置都正确,我运行了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()方法真的被模拟,我添加了一个“debug print”语句:

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

    返回:

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

    我做错了什么?

2 个答案:

答案 0 :(得分:8)

试试这个:

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)

答案 1 :(得分:1)

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

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