可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have been writing tests for one of my django applications and have been looking to get around this problem for quite some time now. I have a view that sends messages using django.contrib.messages
for different cases. The view looks something like the following.
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)
Now, while testing this view client.get
's response does not contain the context
dictionary that contains the messages
as this view uses a redirect. For views that render templates we can get access to the messages list using messages = response.context.get('messages')
. How can we get access messages
for a view that redirects?
回答1:
Use the follow=True
option in the client.get()
call, and the client will follow the redirect. You can then test that the message is in the context of the view you redirected to.
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)
回答2:
You can use get_messages() with response.wsgi_request like this (tested in 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")
回答3:
If your views are redirecting and you use follow=true
in your request to the test client the above doesn't work. I ended up writing a helper function to get the first (and in my case, only) message sent with the response.
@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
You include this within your test class and use it like this:
message = self.getmessage(response)
Where response
is what you get back from a get
or post
to a Client
.
This is a little fragile but hopefully it saves someone else some time.
回答4:
I had the same problem when using a 3rd party app.
If you want to get the messages from a view that returns an HttpResponseRedict (from which you can't access the context) from within another view, you can use get_messages(request)
from django.contrib.messages import get_messages
storage = get_messages(request)
for message in storage:
do_something_with_the_message(message)
This clears the message storage though, so if you want to access the messages from a template later on, add:
storage.used = False
回答5:
Alternative method mocking messages (doesn't need to follow redirect):
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...