Django 测试 - 检查重定向视图的消息

30

我一直在为我的 Django 应用编写测试,并一直在努力解决这个问题。我有一个视图,使用 django.contrib.messages 发送不同情况下的消息。该视图大致如下。

from django.contrib import messages
from django.shortcuts import redirect

import custom_messages

def some_view(request):
    """ This is a sample view for testing purposes.
    """

    some_condition = models.SomeModel.objects.get_or_none(
        condition=some_condition)
    if some_condition:
        messages.success(request, custom_message.SUCCESS)
    else:
        messages.error(request, custom_message.ERROR)
    redirect(some_other_view)

现在,在测试这个视图时,client.get的响应不包含包含messagescontext字典,因为这个视图使用了重定向。对于渲染模板的视图,我们可以使用messages = response.context.get('messages')来访问消息列表。那么,我们如何访问重定向视图中的messages呢?


不确定这是否符合您的需求,但您可以传递get变量以识别发生了什么: redirect(reverse(some_other_view) + '?user_added=true') - Aamir Rind
我实际上已经在我的测试中测试了视图中使用的条件。这里我谈论的是明确测试发送的消息。 - Amyth
5个回答

52

client.get()调用中使用follow=True选项,客户端将跟随重定向。然后您可以测试消息是否在您重定向到的视图的上下文中。

def test_some_view(self):
    # use follow=True to follow redirect
    response = self.client.get('/some-url/', follow=True)

    # don't really need to check status code because assertRedirects will check it
    self.assertEqual(response.status_code, 200)
    self.assertRedirects(response, '/some-other-url/')

    # get message from context and check that expected text is there
    message = list(response.context.get('messages'))[0]
    self.assertEqual(message.tags, "success")
    self.assertTrue("success text" in message.message)

1
谢谢,这个方法可行。不过,使用 follow=True 会改变预期的重定向代码从 302 变成 200,因为它会跟随重定向的视图。 - Amyth
是的,遵循重定向意味着响应具有状态码200。有一个assertRedirects方法,您可以使用它来测试重定向。 - Alasdair
是的,那就是我现在正在使用的 :) - Amyth
这也适用于 client.post() 添加 follow=True - Aaron Lelevier
2
你不能直接使用messages[0]访问消息中的元素,你必须将其转换为列表或元组 - list(messages)。 - bbrik
@bbrik,答案现在已经更新了,很抱歉我没能更早地看到您的评论。 - Alasdair

8
你可以像下面这样使用 response.wsgi_request 和 get_messages() 方法(在 Django 1.10 中测试通过):
from django.contrib.messages import get_messages  
...
def test_view(self):
    response = self.client.get('/some-url/') # you don't need follow=True
    self.assertRedirects(response, '/some-other-url/')
    # each element is an instance of  django.contrib.messages.storage.base.Message
    all_messages = [msg for msg in get_messages(response.wsgi_request)]

    # here's how you test the first message
    self.assertEqual(all_messages[0].tags, "success")
    self.assertEqual(all_messages[0].message, "you have done well")

1
这对我有用,而Alasdair的方法没有显示任何消息。我使用的是Django 1.11。 - Serge Mosin

1
如果您的视图正在重定向,并且在向测试客户端发送请求时使用了follow=true,则上述方法不起作用。我最终编写了一个辅助函数来获取响应中发送的第一条消息(在我的情况下只有一条)。
@classmethod
def getmessage(cls, response):
    """Helper method to return message from response """
    for c in response.context:
        message = [m for m in c.get('messages')][0]
        if message:
            return message

你需要在测试类中包含此内容并像这样使用:

message = self.getmessage(response)

response 是从 Clientgetpost 返回的内容。

这有点脆弱,但希望能为其他人节省一些时间。


2
你不需要使用列表推导式,只需要使用list(messages)或者tuple(messages)即可。 - bbrik

1
我在使用第三方应用时遇到了同样的问题。如果你想从返回HttpResponseRedirect(无法访问上下文)的视图中获取消息,可以在另一个视图中使用get_messages(request)。请注意保留html标签。
from django.contrib.messages import get_messages  

storage = get_messages(request)  
for message in storage:  
    do_something_with_the_message(message)  

这会清除消息存储,如果您想稍后从模板中访问消息,请添加以下内容:
storage.used = False

1

替代的方法模拟消息(无需遵循重定向):

from mock import ANY, patch
from django.contrib import messages

@patch('myapp.views.messages.add_message')
def test_some_view(self, mock_add_message):
    r = self.client.get('/some-url/')
    mock_add_message.assert_called_once_with(ANY, messages.ERROR, 'Expected message.')  # or assert_called_with, assert_has_calls...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接