Skip to content Skip to sidebar Skip to footer

How Can I Unit Test Django Messages?

In my django application, I'm trying to write a unit test that performs an action and then checks the messages in the response. As far as I can tell, there is no nice way of doing

Solution 1:

I found a really easy approach:

response = self.client.post('/foo/')
messages = list(response.context['messages'])
self.assertEqual(len(messages), 1)
self.assertEqual(str(messages[0]), 'my message')

If you need to check for messages on a response that has no context you can use the following:

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

The fallback storage doesn't support indexing, however it is an iterable.

Solution 2:

From django documentation:

Outside of templates, you can use get_messages()

So, you could write something like:

from django.contrib.messages import get_messages

[...]

messages = [m.message for m in get_messages(response.wsgi_request)]
self.assertIn('My message', messages)

Solution 3:

This works for me (displays all messages):

print [m.message for m in list(response.context['messages'])]

Also here are a couple of utility methods I have in a test class inherited from Django's TestCase. If you'd prefer to have them as functions, remove the self arguments and replace self.fail()'s with a raise.

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

defassert_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]

    iflen(matches) == 1:
        msg = matches[0]
        if level isnotNoneand msg.level != level:
            self.fail('There was one matching message but with different''level: %s != %s' % (msg.level, level))

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

defassert_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]

    iflen(matches) > 0:
        self.fail('Message(s) contained text "%s": %s' %
            (text, ", ".join(('"%s"' % m) for m in matches)))

Solution 4:

Update

My original answer was written when django was still 1.1 or so. This answer is no longer relevant. See @daveoncode's answer for a better solution.

Original Answer

I did an experiment to test this. I changed the MESSAGE_STORAGE setting in one of my projects to 'django.contrib.messages.storage.cookie.CookieStorage' and executed a test that I had written to check for messages. It worked.

The key difference from what you were doing is the way I retrieved messages. See below:

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

This may be worth a try.

Solution 5:

Simpler version of the stalemate one:

classTestCaseMessagesMixture(object):
    defassertMessageCount(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)
                )

    defassertMessageEqual(self, response, text):
        """
        Asserts that the response includes the message text.
        """

        messages = [m.message for m in response.context['messages']]

        if text notin messages:
            self.fail(
                'No message with text "%s", messages were: %s' % 
                    (text, messages)
                )

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

Post a Comment for "How Can I Unit Test Django Messages?"