模拟Django Queryset以测试采用查询集的函数

问题描述 投票:30回答:9

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

有这样的事情吗?

python django unit-testing django-queryset django-testing
9个回答
-8
投票

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


15
投票

当然你可以模拟一个QuerySet,你可以模拟任何东西。

您可以自己创建一个对象,并为其提供所需的界面,并让它返回您喜欢的任何数据。从本质上讲,嘲讽只不过是提供一个“测试双重”,就像你的测试目的一样,就像真实的东西一样。

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

class MockQuerySet(object):
    pass

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


12
投票

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


7
投票

对于一个空的Queryset,我只是将none用作keithhackbarth has already stated

但是,要模拟将返回值列表的Queryset,我更喜欢使用带有模型管理器的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()

然而,为了解决这个问题,而不是为return_value设置count,这可以很容易地调整为从list返回的模型实例的all

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

m_queryset.filter.return_value = m_queryset

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


4
投票

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

例如:

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
投票

你看过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/


0
投票

尝试使用django_mock_queries library来模拟数据库访问,并仍然使用一些Django查询集功能,如过滤。

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


0
投票

你可以这样嘲笑:

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

-1
投票

一个第一个建议是将函数分成两部分,一部分用于创建查询集,另一部分用于操作其输出。通过这种方式测试第二部分非常简单。

对于数据库问题,我调查了django是否使用sqlite-in-memory,我发现最新版本的django使用qqlxswpoi中的sqlite -in-memory数据库:

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

模拟QuerySet对象不会让你运用它的完整逻辑。

© www.soinside.com 2019 - 2024. All rights reserved.