我的 Django 项目中有一个实用函数,它接受一个查询集,从中获取一些数据并返回一个结果。我想为这个函数写一些测试。无论如何“模拟”一个查询集?我想创建一个不接触数据库的对象,我可以为它提供一个要使用的值列表(即一些假行),然后它就像一个查询集一样,并允许某人对其进行字段查找/过滤/获取/全部等。
这样的事情已经存在了吗?
我的 Django 项目中有一个实用函数,它接受一个查询集,从中获取一些数据并返回一个结果。我想为这个函数写一些测试。无论如何“模拟”一个查询集?我想创建一个不接触数据库的对象,我可以为它提供一个要使用的值列表(即一些假行),然后它就像一个查询集一样,并允许某人对其进行字段查找/过滤/获取/全部等。
这样的事情已经存在了吗?
当然你可以模拟一个 QuerySet,你可以模拟任何东西。
你可以自己创建一个对象,给它你需要的接口,让它返回你喜欢的任何数据。从本质上讲,模拟只不过是提供了一个“测试替身”,它的作用与真实事物一样足以满足您的测试目的。
入门的低技术方法是定义一个对象:
class MockQuerySet(object):
pass
然后创建其中之一,并将其交给您的测试。测试将失败,可能在AttributeError
. 这将告诉您需要在MockQuerySet
. 重复直到您的对象足够丰富以进行测试。
对于一个空的查询集,我会简单地使用none
,因为keithhackbarth 已经说过。
但是,为了模拟一个将返回值列表的查询集,我更喜欢使用带有模型管理器的模拟。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()
然而,为了解决这个问题,而不是设置一个return_value
for count
,这可以很容易地调整为list
从 返回的模型实例的一个all
。
请注意,通过设置filter
返回模拟查询集来处理链接:
m_queryset.filter.return_value = m_queryset
这将需要应用于被测函数中使用的任何查询集方法,例如exclude
,等。
我遇到了同样的问题,看起来某个好人编写了一个用于模拟 QuerySets 的库,它被称为mock-django,您需要的具体代码在这里https://github.com/dcramer/mock-django /blob/master/mock_django/query.py我认为您可以然后修补模型对象函数以返回您已设置为返回预期内容的这些 QuerySetMock 对象之一!
为此,我使用Django 的 .none() 函数。
例如:
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``.
你看过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/
试试这个django_mock_queries
库,它可以让你模拟数据库访问,并且仍然使用一些 Django 查询集功能,比如过滤。
完全披露:我为该项目贡献了一些功能。
你可以这样模拟:
@patch('django.db.models.query.QuerySet')
def test_returning_distinct_records_for_city(self, mock_qs):
self.assertTrue(mock_qs.called)
第一个建议是将函数分成两部分,一个创建查询集,另一个操作它的输出。以这种方式测试第二部分很简单。
对于数据库问题,我调查了 django 是否使用 sqlite-in-memory,我发现最新版本的 django 使用 sqlite -in-memory 数据库,来自The django unittest page:
当使用 SQLite 数据库引擎时,测试将默认使用内存数据库(即,数据库将在内存中创建,完全绕过文件系统!)。
模拟 QuerySet 对象不会让您使用它的全部逻辑。
不是我所知道的,但为什么不使用实际的查询集呢?测试框架全部设置为允许您在测试中创建示例数据,并且每次测试都重新创建数据库,因此似乎没有任何理由不使用真实的东西。