【问题标题】:Mocking a Django Queryset in order to test a function that takes a queryset模拟 Django 查询集以测试采用查询集的函数
【发布时间】:2011-11-13 20:17:43
【问题描述】:

我的 Django 项目中有一个实用函数,它接受一个查询集,从中获取一些数据并返回一个结果。我想为这个函数写一些测试。无论如何“模拟”一个查询集?我想创建一个不接触数据库的对象,我可以为它提供一个要使用的值列表(即一些假行),然后它就像一个查询集一样,并允许某人对其进行字段查找/过滤/获取/全部等。

这样的东西已经存在了吗?

【问题讨论】:

    标签: python django unit-testing django-queryset django-testing


    【解决方案1】:

    当然你可以模拟一个查询集,你可以模拟任何东西。

    你可以自己创建一个对象,给它你需要的接口,让它返回你喜欢的任何数据。从本质上讲,模拟只不过是提供一个“测试替身”,它的行为与真实事物一样足以满足您的测试目的。

    入门的低技术方法是定义一个对象:

    class MockQuerySet(object):
        pass
    

    然后创建其中之一,并将其​​交给您的测试。测试将失败,可能在AttributeError 上。这将告诉您需要在 MockQuerySet 上实现什么。重复,直到您的对象足够丰富,可以进行测试。

    【讨论】:

    • 是的,我知道模拟它相对简单。但我想知道在我去实现所有这些方法之前,是否有人创建了所有过滤器/排除方法。 :)
    【解决方案2】:

    对于一个空的查询集,我会简单地将none 用作keithhackbarth has already stated

    但是,为了模拟将返回值列表的查询集,我更喜欢使用模型经理的 Mockspec。作为示例(Python 2.7 风格 - I've used the external Mock library),这是一个简单的测试,其中 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()
    

    但是,为了解决这个问题,可以轻松地将其调整为从all 返回的模型实例的list,而不是为return_value 设置return_value

    请注意,通过设置 filter 来处理链接以返回模拟查询集:

    m_queryset.filter.return_value = m_queryset
    

    这需要应用于被测函数中使用的任何查询集方法,例如exclude

    【讨论】:

      【解决方案3】:

      我也有同样的问题,看起来某个好人编写了一个用于模拟 QuerySets 的库,它被称为mock-django,您需要的具体代码在这里https://github.com/dcramer/mock-django/blob/master/mock_django/query.py 我想您可以然后修补您对对象函数进行建模以返回您设置的这些 QuerySetMock 对象之一,以返回预期的内容!

      【讨论】:

      • 工作愉快,谢谢。这应该是公认的答案!
      【解决方案4】:

      为此,我使用Django's .none() function

      例如:

      class Location(models.Model):
        name = models.CharField(max_length=100)
      mock_locations = Location.objects.none()
      

      这是Django自己内部测试用例中经常使用的方法。基于代码中的cmets

      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``.
      

      【讨论】:

      • 直到.none!谢谢 - 这非常有用。
      【解决方案5】:

      你看过 FactoryBoy 吗? https://factoryboy.readthedocs.io/en/latest/orms.html 它是一个支持 django orm 的夹具替换工具 - 工厂基本上生成类似 orm 的对象(在内存中或在测试数据库中)。

      这是一篇很棒的入门文章:https://www.caktusgroup.com/blog/2013/07/17/factory-boy-alternative-django-testing-fixtures/

      【讨论】:

        【解决方案6】:

        试试django_mock_queries library,它可以让您模拟数据库访问,并且仍然使用一些 Django 查询集功能,例如过滤。

        完全披露:我为该项目贡献了一些功能。

        【讨论】:

          【解决方案7】:

          你可以这样模拟:

          @patch('django.db.models.query.QuerySet')
          def test_returning_distinct_records_for_city(self, mock_qs):
              self.assertTrue(mock_qs.called)
          

          【讨论】:

            【解决方案8】:

            第一个建议是将函数分成两部分,一个创建查询集 和一个操纵它的输出。这样测试第二部分就很简单了。

            对于数据库问题,我调查了 django 是否使用 sqlite-in-memory,我发现 最新版本的 django 使用 sqlite -in-memory 数据库,来自The django unittest page

            使用 SQLite 数据库引擎时,测试默认使用 内存数据库(即,数据库将在内存中创建, 完全绕过文件系统!)。

            模拟 QuerySet 对象不会让您充分利用它的逻辑。

            【讨论】:

              【解决方案9】:

              我不知道,但为什么不使用实际的查询集呢?测试框架全部设置为允许您在测试中创建示例数据,并且每次测试都会重新创建数据库,因此似乎没有任何理由不使用真实的东西。

              【讨论】:

              • mock QuerySet 的原因是为了让测试更快,同时也降低了运行的复杂度。
              • @NedBatchelder 所说的其他一些优点是,您的测试很可能会由更多开发人员(您或您的团队)更频繁地运行并且失败更快,这使您可以更频繁地发现错误和更改.在测试视图时,我倾向于模拟模型管理器上的方法,但我想了解更多关于模拟 QuerySet 和其他改进测试的策略。
              • 我同意 Ned 在速度方面的观点,而且如果单独使用 Django ORM(可能是一个不寻常的用例),这将变得不那么可靠——“自动”测试数据库不是为你创建的,而且你永远不应该在实时数据库上运行测试用例。
              • 实际QuerySet不保证正确,尽量使用简单的mocking QuerySet,避免两者同时处于错误状态的情况。
              猜你喜欢
              • 2013-11-26
              • 2017-05-05
              • 2018-04-16
              • 1970-01-01
              • 2012-12-15
              • 2020-06-05
              • 1970-01-01
              • 2012-10-04
              • 2020-11-14
              相关资源
              最近更新 更多