如何对 django 消息进行单元测试?
Posted
技术标签:
【中文标题】如何对 django 消息进行单元测试?【英文标题】:How can I unit test django messages? 【发布时间】:2011-02-23 06:23:34 【问题描述】:在我的 django 应用程序中,我正在尝试编写一个单元测试来执行一个操作,然后检查响应中的消息。
据我所知,没有很好的方法可以做到这一点。
我使用的是CookieStorage存储方式,我想做类似下面的事情:
response = self.client.post('/do-something/', follow=True)
self.assertEquals(response.context['messages'][0], "fail.")
问题是,我得到的只是一个
print response.context['messages']
<django.contrib.messages.storage.cookie.CookieStorage object at 0x3c55250>
我怎样才能把它变成有用的东西,还是我做错了?
谢谢, 丹尼尔
【问题讨论】:
这行得通,但是……真的吗? response.context['messages']._get()[0][0].__dict__['message'] 您可以尝试将这段不那么漂亮的代码封装在漂亮的函数assert_has_message(response, msg_text)
中,然后在您想要的任何地方使用它。如果您能找到更好的方法来访问消息,您只需在一处修改函数即可。
@nailxx,是的,这基本上就是我所做的,但这让我感觉不舒服:)
这也有效:messages_list = CookieStorage(response)._decode(response.cookies['messages'].value)
这为您提供了 django.contrib.messages.storage.base.Message 对象的列表。
@dvydra 如果你还在,你可能想改变接受的答案
【参考方案1】:
我找到了一个非常简单的方法:
response = self.client.post('/foo/')
messages = list(response.context['messages'])
self.assertEqual(len(messages), 1)
self.assertEqual(str(messages[0]), 'my message')
如果您需要检查没有上下文的响应中的消息,您可以使用以下命令:
from django.contrib.messages import get_messages
messages = list(get_messages(response.wsgi_request))
self.assertEqual(len(messages), 1)
self.assertEqual(str(messages[0]), 'my message')
后备存储不支持索引,但它是可迭代的。
【讨论】:
我还发现self.assertEqual(m[0].message, 'my message')
有效
如果您需要在没有上下文的响应中检查消息(例如重定向),您可以使用list(r.wsgi_request._messages)
看起来 [0] 不适用于新版本:*** TypeError: 'FallbackStorage' object does not support indexing
。但是,它是一个 Iterable,您可以使用任何 for m in messages
构造。
@Jonathan 在测试用例中的交互式调试器 (import ipdb; ipdb.set_trace()
) 中使用 dir()
。
@Jonathan docs.djangoproject.com/en/2.0/_modules/django/contrib/messages/…【参考方案2】:
来自django documentation:
在模板之外,你可以使用 get_messages()
所以,你可以这样写:
from django.contrib.messages import get_messages
[...]
messages = [m.message for m in get_messages(response.wsgi_request)]
self.assertIn('My message', messages)
【讨论】:
【参考方案3】:这对我有用(显示所有消息):
print [m.message for m in list(response.context['messages'])]
这里还有一些我在从 Django 的 TestCase 继承的测试类中拥有的实用方法。如果您希望将它们用作函数,请删除 self
参数并将 self.fail()
替换为 raise
。
def assert_message_count(self, response, expect_num):
"""
Asserts that exactly the given number of messages have been sent.
"""
actual_num = len(response.context['messages'])
if actual_num != expect_num:
self.fail('Message count was %d, expected %d' %
(actual_num, expect_num))
def assert_message_contains(self, response, text, level=None):
"""
Asserts that there is exactly one message containing the given text.
"""
messages = response.context['messages']
matches = [m for m in messages if text in m.message]
if len(matches) == 1:
msg = matches[0]
if level is not None and msg.level != level:
self.fail('There was one matching message but with different'
'level: %s != %s' % (msg.level, level))
return
elif len(matches) == 0:
messages_str = ", ".join('"%s"' % m for m in messages)
self.fail('No message contained text "%s", messages were: %s' %
(text, messages_str))
else:
self.fail('Multiple messages contained text "%s": %s' %
(text, ", ".join(('"%s"' % m) for m in matches)))
def assert_message_not_contains(self, response, text):
""" Assert that no message contains the given text. """
messages = response.context['messages']
matches = [m for m in messages if text in m.message]
if len(matches) > 0:
self.fail('Message(s) contained text "%s": %s' %
(text, ", ".join(('"%s"' % m) for m in matches)))
【讨论】:
仅适用于显式 ResponseContext 或 TemplateResponse(它试图构造一个 ResponseContext)。【参考方案4】:更新
我最初的答案是在 django 还是 1.1 左右的时候写的。这个答案不再相关。请参阅@daveoncode 的answer 以获得更好的解决方案。
原答案
我做了一个实验来测试这个。我将其中一个项目中的MESSAGE_STORAGE
设置更改为'django.contrib.messages.storage.cookie.CookieStorage'
,并执行了我为检查消息而编写的测试。有效。
与您所做的主要区别在于我检索消息的方式。见下文:
def test_message_sending(self):
data = dict(...)
response = self.client.post(reverse('my_view'), data)
messages = self.user.get_and_delete_messages()
self.assertTrue(messages)
self.assertEqual('Hey there!', messages[0])
这个可能值得一试。
【讨论】:
user.get_and_delete_messages() 在 Django 1.2 中已弃用【参考方案5】:相持一的简单版本:
class TestCaseMessagesMixture(object):
def assertMessageCount(self, response, expect_num):
"""
Asserts that exactly the given number of messages have been sent.
"""
actual_num = len(response.context['messages'])
if actual_num != expect_num:
self.fail('Message count was %d, expected %d' %
(actual_num, expect_num)
)
def assertMessageEqual(self, response, text):
"""
Asserts that the response includes the message text.
"""
messages = [m.message for m in response.context['messages']]
if text not in messages:
self.fail(
'No message with text "%s", messages were: %s' %
(text, messages)
)
def assertMessageNotEqual(self, response, text):
"""
Asserts that the response does not include the message text.
"""
messages = [m.message for m in response.context['messages']]
if text in messages:
self.fail(
'Message with text "%s" found, messages were: %s' %
(text, messages)
)
【讨论】:
这并不完全相同,因为我的版本会检查给定文本是否包含(不等于)任何/没有消息。我更喜欢这种方式,这样我只需在测试用例中输入消息的关键部分,并允许更新消息文本而不会破坏测试。【参考方案6】:测试助手用于验证响应消息的数量和内容
def get_response_messages(self, response):
from django.contrib.messages import get_messages
return list(get_messages(response.wsgi_request))
def check_response_messages(self, response, message_index=None, message_value=None, exp_count=None):
messages = self.get_response_messages(response)
if exp_count is not None:
self.assertEqual(len(messages), exp_count)
if message_index is not None:
message = messages[message_index]
self.assertIn(message_value, str(message))
可以这样使用
message_value = "You can not switch to another type of account"
self.check_response_messages(response, exp_count=1, message_index=0, message_value=message_value)
【讨论】:
以上是关于如何对 django 消息进行单元测试?的主要内容,如果未能解决你的问题,请参考以下文章