如何对Django邮件进行单元测试?


78

在我的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']
dvydra

5
您可以尝试将这些不太漂亮的代码封装在漂亮的函数中assert_has_message(response, msg_text),然后在您想要的任何地方使用它。如果您找到访问消息的更好方法,则只需在一个地方修改该功能即可。
nkrkv,2010年

@nailxx,是的,基本上这就是我所做的,但是这让我感到不适:)
dvydra 2010年

3
这也起作用:messages_list = CookieStorage(response)._decode(response.cookies['messages'].value) 这为您提供了django.contrib.messages.storage.base.Message对象的列表。
dvydra

@dvydra如果您仍然在身边,则可能要更改接受的答案
OrangeDog

Answers:


89

我发现了一个非常简单的方法:

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

后备存储不支持索引,但是它是可迭代的。


2
我还发现,self.assertEqual(m[0].message, 'my message')作品
亚伦Lelevier

6
如果您需要检查无上下文响应的消息(例如重定向),则可以使用list(r.wsgi_request._messages)
BenjaminGolder

6
看来[0]在新版本上不起作用:*** TypeError: 'FallbackStorage' object does not support indexing。但是,它是一个Iterable,您可以使用任何for m in messages构造。
Dunatotatos

2
@Jonathan通过dir()在测试用例内部的交互式调试器(import ipdb; ipdb.set_trace())中使用。
BenjaminGolder


17

这对我有用(显示所有消息):

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

2
仅与显式ResponseContext或TemplateResponse(试图构造ResponseContext)一起使用。
pkoch 2011年

17

Django文档

在模板之外,可以使用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

更新资料

我的原始答案是在django仍为1.1左右时写的。这个答案不再重要。请参阅@daveoncode的答案以获得更好的解决方案。

原始答案

我做了一个实验来测试。我将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])

可能值得一试。


27
user.get_and_delete_messages()在Django 1.2中已弃用
Dave

0

僵局的简单版本:

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

这并不完全相同,因为我的版本检查给定的文本包含在(不等于)任何/无消息中。我更喜欢这种方式,因此我只需要在测试用例中输入消息的关键部分,并允许在不破坏测试的情况下更新消息文本。
anttikoo 2012年

0

测试帮助程序以验证响应消息的数量和内容

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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.