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

Ror*_*ory 32 python django unit-testing django-queryset django-testing

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

有这样的事情吗?

Ned*_*der 16

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

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

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

class MockQuerySet(object):
    pass
Run Code Online (Sandbox Code Playgroud)

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

  • 是的,我知道嘲笑它相对简单.但是我想知道是否有其他人已经创建了所有的过滤/排除方法,然后才开始实施所有这些方法.:) (7认同)

Ctr*_*spc 13

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


jam*_*esc 9

对于空的QuerySet,我会去简单地使用none作为keithhackbarth已经说明.

然而,嘲笑一个QuerySet,将返回值的列表,我更喜欢使用一个模拟spec模型的经理.作为一个例子(Python 2.7样式 - 我使用了外部Mock库),这里是一个简单的测试,其中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()
Run Code Online (Sandbox Code Playgroud)

但是,为了解决这个问题,可以很容易地将其调整为从中返回的模型实例,而不是设置return_valuefor .countlistall

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

m_queryset.filter.return_value = m_queryset
Run Code Online (Sandbox Code Playgroud)

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


kei*_*rth 5

为此,我使用Django 的 .none() 函数

例如:

class Location(models.Model):
  name = models.CharField(max_length=100)
mock_locations = Location.objects.none()
Run Code Online (Sandbox Code Playgroud)

这是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``.
Run Code Online (Sandbox Code Playgroud)


Dan*_*man -21

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

  • 模拟 QuerySet 的原因是为了使测试更快,并降低运行它们的复杂性。 (40认同)
  • @NedBatchelder 所说的其他一些优点是,您的测试很可能会由更多开发人员(您或您的团队)更频繁地运行,并且失败得更快,这使您可以更频繁地捕获错误和更改。在测试视图时,我倾向于模拟模型管理器上的方法,但我想了解有关模拟 QuerySet 和其他改进测试的策略的更多信息。 (2认同)
  • 我同意 Ned 在速度方面的观点,而且如果使用独立的 Django ORM(可能是一个不寻常的用例),这会变得不太可靠 - 不会为您创建“自动”测试数据库,并且您永远不应该运行测试用例在实时数据库上。 (2认同)