Як я можу протестувати повідомлення 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

@nailxx, так, це в основному те, що я зробив, але мені стає погано :)
dvydra

3
Це також працює: messages_list = CookieStorage(response)._decode(response.cookies['messages'].value) Це дає вам список об’єктів django.contrib.messages.storage.base.Message.
dvydra

@dvydra, якщо ти все ще поруч, можливо, ти захочеш змінити прийняту відповідь
OrangeDog

Відповіді:


90

Я знайшов дуже простий підхід:

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')працює
Аарон Лелев'є

6
якщо вам потрібно перевірити наявність повідомлень у відповіді, що не має контексту (наприклад, переспрямування), ви можете використовуватиlist(r.wsgi_request._messages)
BenjaminGolder

6
Схоже , [0] не працює на нових версіях: *** TypeError: 'FallbackStorage' object does not support indexing. Однак це Iterable, і ви можете використовувати будь-яку for m in messagesконструкцію.
Дунатотатос,

2
@Jonathan, використовуючи dir()всередині інтерактивний налагоджувач ( import ipdb; ipdb.set_trace()) всередині тесту.
BenjaminGolder


19

З документації 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)


17

Це працює для мене (відображає всі повідомлення):

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

Також тут є кілька корисних методів, які я маю в тестовому класі, успадкованому від TestCase від Django. Якщо ви віддаєте перевагу використовувати їх як функції, видаліть 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

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
Дейв,

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

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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.