模拟Django查询集以测试接受查询集的函数

39 投票
9 回答
32630 浏览
提问于 2025-04-17 01:54

我在我的Django项目里有一个工具函数,它接收一个查询集(queryset),从中获取一些数据并返回结果。我想为这个函数写一些测试。有没有办法可以“模拟”一个查询集?我想创建一个对象,它不接触数据库,我可以给它提供一组值(也就是一些虚假的数据行),然后它就能像查询集一样工作,让人可以对它进行字段查找、过滤、获取所有数据等操作。

有没有类似的东西已经存在了?

9 个回答

15

当然,你可以模拟一个查询集(QuerySet),其实你可以模拟任何东西。

你可以自己创建一个对象,给它你需要的接口,然后让它返回你想要的任何数据。简单来说,模拟就是提供一个“测试替身”,这个替身在测试中表现得足够像真实的东西。

一个简单的入门方法是先定义一个对象:

class MockQuerySet(object):
    pass

然后创建一个这样的对象,把它交给你的测试。测试可能会失败,通常是因为出现了AttributeError错误。这会告诉你在你的MockQuerySet中需要实现什么。重复这个过程,直到你的对象足够丰富,能够满足测试的需求。

17

对于一个空的查询集,我会直接使用 none,就像 keithhackbarth 已经提到的那样

不过,如果想要模拟一个查询集,让它返回一系列值,我更喜欢用一个 Mock 对象,并且指定它的 spec 为模型的管理器。举个例子(这是 Python 2.7 的写法 - 我使用了外部的 Mock 库),下面是一个简单的测试,查询集经过过滤后再进行计数:

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()

不过,为了满足问题的要求,除了为 count 设置 return_value,还可以很简单地调整为从 all 返回的模型实例的 list

注意,链式调用是通过设置 filter 来返回模拟的查询集来处理的:

m_queryset.filter.return_value = m_queryset

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

-21

我不知道有没有这样的情况,但为什么不直接用真实的数据查询呢?测试框架已经准备好了,可以让你在测试中创建示例数据,而且每次测试时数据库都会被重新创建,所以用真实的数据似乎没有什么不妥。

撰写回答