Reputation: 143
I can't seem to get a unit test to check if Messages are rendering properly in my template. As per my template, I'm not getting any output where the messages should be listed.
I'm using pytest 6.2.5 and Django 3.1.13 if that's helpful. Here is my test code:
import pytest
from django.contrib import messages
from django.contrib.messages.middleware import MessageMiddleware
from django.contrib.sessions.middleware import SessionMiddleware
from django.test import RequestFactory
from django.views.generic import TemplateView
pytestmark = pytest.mark.django_db
class TestBaseTemplate:
def test_messages_middleware(self):
request = RequestFactory().get("/")
view = TemplateView.as_view(template_name="base.html")
middleware1 = SessionMiddleware()
middleware1.process_request(request)
request.session.save()
middleware2 = MessageMiddleware()
middleware2.process_request(request)
foobar_msg = "Hello, world!"
messages.info(request, foobar_msg)
request.session.save()
response = view(request)
response.render()
content = response.content.decode("utf-8")
# This assert fails
assert foobar_msg in content
My message block in the template (base.html) is straight-forward (and does work in views that send messages):
{% if messages %}
<div>
{% for message in messages %}
<p>{{ message }}</p>
{% endfor %}
</div>
{% endif %}
I can see through debugging that before you get to rendering the response that the "Hello, world!" message is being noted properly in the request via the Session/Message middleware, it's just not being rendered in the response.
Any ideas on what I'm missing? Do I need to do something with a context preprocessor before rendering the content?
Thanks in advance for any advice!
Upvotes: 0
Views: 505
Reputation: 143
There might be a more straight-forward method, but the trick for me was to explicitly include the messages in the view context.
Here is how I managed to get the view properly rendering messages. Note that I'm using pytest, with a standard RequestFactory fixture. It should be pretty trivial to convert this code to a unittest
paradigm.
from typing import Any
import pytest
from django.contrib import messages
from django.contrib.messages.middleware import MessageMiddleware
from django.contrib.sessions.middleware import SessionMiddleware
from django.test import RequestFactory
from django.views.generic import TemplateView
pytestmark = pytest.mark.django_db
class TestTemplateMessages:
"""Tests for the messages rendering in template."""
class SampleTemplate(TemplateView):
"""Template for testing."""
template_name = "frame_with_messages_block.html"
def __init__(self, **kwargs: Any) -> None:
"""Setup request middleware."""
super().__init__(**kwargs)
middleware1 = SessionMiddleware()
middleware1.process_request(self.request)
middleware2 = MessageMiddleware()
middleware2.process_request(self.request)
self.request.session.save()
def get_context_data(self, **kwargs: str) -> dict[str, Any]:
"""Add current user to context."""
context = super().get_context_data(**kwargs)
context["messages"] = messages.get_messages(self.request)
return context
def test_messages_middleware(self, rf: RequestFactory) -> None:
"""Test messages are rendering properly in templates."""
request = rf.get("/")
info_message = "Hello, world!"
error_message = "Hello, error?"
expected_messages = [info_message, error_message]
view = self.SampleTemplate(request=request)
messages.info(request, info_message)
messages.error(request, error_message)
# Test messages are part of the request middleware
actual_messages = [str(m) for m in messages.get_messages(request)]
assert actual_messages == expected_messages
# Test messages are present in the response HTML
response = view.get(request)
response.render() # type: ignore
content = response.content.decode("utf-8")
for message in expected_messages:
assert message in content
Upvotes: 1