在Django中使用patch装饰器模拟模块时出错
我正在学习Django中的模拟(mocking),但是对补丁(patching)这个概念有点搞不清楚。
在我的测试中,test_get_name这个测试通过了,但test_patched_get_name却失败了,错误信息是ValueError: Object is of type 'MagicMock', but must be a Django Model, Manager, or QuerySet
。
这是我的文件:
utils.py
def get_name(clientid):
c = get_object_or_404(Client, pk=clientid)
c_name = c.get_name()
return c_name
tests.py
class ClientTests(unittest.TestCase):
def setUp(self):
self.c_instance = mock.Mock(spec=Client, pk=1)
self.c_instance.name = "Test"
def test_get_name(self):
self.assertEqual(Client.get_name(self.c_instance), "Test")
def test_patched_get_name(self):
with mock.patch('myapp.utils.Client') as self.c_instance:
from myapp.utils import get_name
self.assertEqual(get_name(self.c_instance.pk), "Test")
models.py
class Client(models.Model):
name = models.CharField(max_length=200)
def get_name(self):
return self.name
我猜get_object_or_404
需要的是一个模型,而不是一个模拟对象,但我不太确定该怎么做。
完整的错误追踪信息:
======================================================================
ERROR: test_patched_get_name (myapp.tests.ClientTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "pathto/myapp/tests.py", line 63, in test_patched_get_name
self.assertEqual(get_name(self.c_instance.pk)
File "pathto/myapp/views.py", line 36, in get_name
cu = get_object_or_404(Client, pk=clientid)
File "pathto/lib/python2.7/site-packages/django/shortcuts/__init__.py", line 111, in get_object_or_404
queryset = _get_queryset(klass)
File "pathto/lib/python2.7/site-packages/django/shortcuts/__init__.py", line 97, in _get_queryset
"Manager, or QuerySet" % klass__name)
ValueError: Object is of type 'MagicMock', but must be a Django Model, Manager, or QuerySet
----------------------------------------------------------------------
1 个回答
1
如果你想测试 get_name 这个函数,我觉得你应该先对 Django 的 get_object_or_404 进行一些修改。简单来说,就是在测试的时候,当调用 get_name 时,不是去调用 Django 的 get_object_or_404,而是调用一个模拟的对象——也就是你修改过的那个函数——这个模拟函数会返回你预先设定好的值。这样,你的测试就只需要验证 get_name 是否正常工作,并且返回正确的值。实际上并不需要真的去调用 Django 的 get_object_or_404(因为那不是你要测试的内容)。
我觉得你的测试应该像这样:
# make sure you import "patch" from mock
from mock import patch
# Patch the "get_object_or_404" function used inside "get_name"
@patch('myapp.utils.get_object_or_404')
def test_patched_get_name(self, mock_get_object_or_404):
# The patched function is supplied as a parameter to your test function
# -- you can give it any name you want (in this case, it's
# "mock_get_object_or_404").
from myapp.utils import get_name
# Set mock function's return value -- the patched function here
# will return the mock "Client" object.
mock_get_object_or_404.return_value = self.c_instance
# Now compare the return value from the "get_name" function to
# see if it's the same as the dummy client's name.
returned_value = get_name(self.c_instance.pk)
expected_value = self.c_instance.name
self.assertEqual(returned_value, expected_value)