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">

Asked By: Emilio Conte

||

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

Answered By: Emilio Conte
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.