Testing contrib.messages from an UpdateView with pytest
Question:
I’m testing an update view with pytest. The following test runs ok. The lang_src
instance is correctly updated.
class TestAvailableUpdateView:
url = "dashboard.staff:available-update"
def test_authenticated_staff(self, staff_client, lang_src):
url = reverse(self.url, kwargs={'pk': lang_src.pk})
data = {'code': 'it', 'pk': lang_src.pk}
resp = staff_client.post(url, data=data)
assert resp.status_code == 302
lang_src.refresh_from_db()
assert lang_src.code == 'it'
staff_client
is defined in factories.py
:
from django.test import Client
@pytest.fixture()
def staff_user(db):
return User.objects.create_user(
username='[email protected]',
password='password', is_staff=True,
is_active=True)
@pytest.fixture()
def staff_client(staff_user):
"""Return a Django test client logged in as a staff user."""
client = Client()
client.login(username=staff_user.username, password='password')
return client
To test messages
from contrib.messages
, I’ve setup folllow=True
as following.
def test_updated_message(self, staff_client, lang_src):
url = reverse(self.url, kwargs={'pk': lang_src.pk})
data = {'code': 'it', 'pk': lang_src.pk}
resp = staff_client.post(url, data=data, follow=True)
assert resp.status_code == 200
message = list(resp.context.get('messages'))[0]
assert message == 'Languages updated'
I’m returned a TemplateResponse
where resp.context['messages']
doesn’t contain any message.
Here is the view:
class AvailableLanguageUpdateView(UpdateView):
model = AvailableLanguage
template_name = "dashboard_staff/languages/lang_form.html"
extra_context = {"available_lang_menu": True}
fields = ['code']
success_msg = _("Languages updated")
def form_valid(self, form):
messages.success(self.request, self.success_msg)
return super().form_valid(form)
And the model has a get_absolute_url()
method that redirects to a listview which works fine. And I must say that the update works fine too.
3rd EDIT
When I run test, I got these exceptions:
AssertionError: assert <django.contrib.messages.storage.base.Message object at 0x7f56f02c0dd8> == ‘Languages updated’
And if I test the resp.url
, the exception is:
AttributeError: ‘TemplateResponse’ object has no attribute ‘url’
The resp
is:
<TemplateResponse status_code=200, "text/html; charset=utf-8">
Answers:
I think the solution worth to be mentioned because many solutions using response.context.get('messages')
doesn’t work.
I found out that messages
are stored in wsgi_request
. So, in order to test your success_message
you could do something like:
class TestAvailableUpdateView:
url = "dashboard.staff:available-update"
def test_updated_message(self, staff_client, lang_src):
url = reverse(self.url, kwargs={'pk': lang_src.pk})
data = {'code': 'it', 'pk': lang_src.pk}
resp = staff_client.post(url, data=data, follow=True)
assert resp.status_code == 200
messages = [m.message for m in get_messages(resp.wsgi_request)]
assert 'Languages updated' in messages
I’m testing an update view with pytest. The following test runs ok. The lang_src
instance is correctly updated.
class TestAvailableUpdateView:
url = "dashboard.staff:available-update"
def test_authenticated_staff(self, staff_client, lang_src):
url = reverse(self.url, kwargs={'pk': lang_src.pk})
data = {'code': 'it', 'pk': lang_src.pk}
resp = staff_client.post(url, data=data)
assert resp.status_code == 302
lang_src.refresh_from_db()
assert lang_src.code == 'it'
staff_client
is defined in factories.py
:
from django.test import Client
@pytest.fixture()
def staff_user(db):
return User.objects.create_user(
username='[email protected]',
password='password', is_staff=True,
is_active=True)
@pytest.fixture()
def staff_client(staff_user):
"""Return a Django test client logged in as a staff user."""
client = Client()
client.login(username=staff_user.username, password='password')
return client
To test messages
from contrib.messages
, I’ve setup folllow=True
as following.
def test_updated_message(self, staff_client, lang_src):
url = reverse(self.url, kwargs={'pk': lang_src.pk})
data = {'code': 'it', 'pk': lang_src.pk}
resp = staff_client.post(url, data=data, follow=True)
assert resp.status_code == 200
message = list(resp.context.get('messages'))[0]
assert message == 'Languages updated'
I’m returned a TemplateResponse
where resp.context['messages']
doesn’t contain any message.
Here is the view:
class AvailableLanguageUpdateView(UpdateView):
model = AvailableLanguage
template_name = "dashboard_staff/languages/lang_form.html"
extra_context = {"available_lang_menu": True}
fields = ['code']
success_msg = _("Languages updated")
def form_valid(self, form):
messages.success(self.request, self.success_msg)
return super().form_valid(form)
And the model has a get_absolute_url()
method that redirects to a listview which works fine. And I must say that the update works fine too.
3rd EDIT
When I run test, I got these exceptions:
AssertionError: assert <django.contrib.messages.storage.base.Message object at 0x7f56f02c0dd8> == ‘Languages updated’
And if I test the resp.url
, the exception is:
AttributeError: ‘TemplateResponse’ object has no attribute ‘url’
The resp
is:
<TemplateResponse status_code=200, "text/html; charset=utf-8">
I think the solution worth to be mentioned because many solutions using response.context.get('messages')
doesn’t work.
I found out that messages
are stored in wsgi_request
. So, in order to test your success_message
you could do something like:
class TestAvailableUpdateView:
url = "dashboard.staff:available-update"
def test_updated_message(self, staff_client, lang_src):
url = reverse(self.url, kwargs={'pk': lang_src.pk})
data = {'code': 'it', 'pk': lang_src.pk}
resp = staff_client.post(url, data=data, follow=True)
assert resp.status_code == 200
messages = [m.message for m in get_messages(resp.wsgi_request)]
assert 'Languages updated' in messages