Ray*_*Ray 2 django unit-testing mocking django-models
模拟测试库是Django的一个主题,我似乎无法理解.例如,在下面的代码中,为什么我在单元测试中创建的模拟User实例不出现在我在'get_user_ids'方法中查询的User对象中?如果我通过调用调用停止'get_user_ids'方法中的测试并执行"User.objects.all()",则用户查询集中没有任何内容,并且测试失败.我是不是要创建三个模拟User实例来查询UserProxy的静态方法?
我正在使用Django 1.6和Postgres 9.3并使用命令"python manage.py test -s apps.profile.tests.model_tests:TestUserProxy"运行测试.
谢谢!
# apps/profile/models.py
from django.contrib.auth.models import User
class UserProxy(User):
class Meta:
proxy = True
@staticmethod
def get_user_ids(usernames):
debug()
user_ids = []
for name in usernames:
try:
u = User.objects.get(username__exact=name)
user_ids.append(u.id)
except ObjectDoesNotExist:
logger.error("We were unable to find '%s' in a list of usernames." % name)
return user_ids
# apps/profile/tests/model_tests.py
from django.test import TestCase
from django.contrib.auth.models import User
from mock import Mock
from apps.profile.models import UserProxy
class TestUserProxy(TestCase):
def test_get_user_ids(self):
u1 = Mock(spec=User)
u1.id = 1
u1.username = 'user1'
u2 = Mock(spec=User)
u2.id = 2
u2.username = 'user2'
u3 = Mock(spec=User)
u3.id = 3
u3.username = 'user3'
usernames = [u1.username, u2.username, u3.username]
expected = [u1.id, u2.id, u3.id]
actual = UserProxy.get_user_ids(usernames)
self.assertEqual(expected, actual)
Run Code Online (Sandbox Code Playgroud)
模拟是很棒的测试,并且可以导致非常干净的测试,但是它会受到一些影响(a)在开始时有点狡猾,并且(b)经常需要一些努力来设置模拟对象然后注入/使用在正确的地方.
您为用户创建的模拟对象是看起来像Django User模型对象的对象,但它们不是实际的模型对象,因此不会被放入数据库中.
要使测试正常工作,您有两种选择,具体取决于您要编写的测试类型.
单元测试 - 模拟从数据库返回的数据
第一种选择是使其作为单元测试工作,即get_user_ids与数据库层隔离地测试方法.为此,您需要模拟调用,User.objects.get(username__exact=name)以便它返回您在测试中创建的三个模拟对象.这将是更正确的方法(因为最好单独测试代码单元),但是它将涉及比下面的替代方案更多的工作.
实现这一目标的一种方法是首先将用户查找分离到apps/profile/models.py中的自己的功能:
def get_users_by_name(name):
return User.objects.get(username__exact=name)
Run Code Online (Sandbox Code Playgroud)
这需要在你的函数被调用,通过更换呼叫Users.objects.get(username__exact=name)用get_users_by_name(name).然后,您可以修改测试以修补函数,如下所示:
from django.test import TestCase
from django.contrib.auth.models import User
from mock import Mock, patch
from apps.profile.models import UserProxy
class TestUserProxy(TestCase):
@patch('apps.profile.models.get_user_by_name')
def test_get_user_ids(self, mock_get_user_by_name):
u1 = Mock(spec=User)
u1.id = 1
u1.username = 'user1'
u2 = Mock(spec=User)
u2.id = 2
u2.username = 'user2'
u3 = Mock(spec=User)
u3.id = 3
u3.username = 'user3'
# Here is where we wire up the mocking - we take the patched method to return
# users and tell it that, when it is called, it must return the three mock
# users you just created.
mock_get_user_by_name.return_value = [u1, u2, u3]
usernames = [u1.username, u2.username, u3.username]
expected = [u1.id, u2.id, u3.id]
actual = UserProxy.get_user_ids(usernames)
self.assertEqual(expected, actual)
Run Code Online (Sandbox Code Playgroud)
集成测试 - 创建真实用户对象
第二种方法是将其修改为集成测试,即测试这个代码单元以及与数据库的交互.这有点不太干净,因为您现在将方法上的测试暴露给失败的可能性,因为不同的代码单元(即与数据库交互的Django代码)存在问题.但是,这确实使测试的设置更加简单,并且实用可能是适合您的方法.
要执行此操作,只需删除您创建的模拟,并在测试过程中在数据库中创建实际用户.
| 归档时间: |
|
| 查看次数: |
5594 次 |
| 最近记录: |