6

I'm trying to test an UpdateView that adds a message to the redirected success page. It seems my issue comes from messages because of pytest returns:

django.contrib.messages.api.MessageFailure: You cannot add messages without installing django.contrib.messages.middleware.MessageMiddleware

My test code is:

def test_authenticated_staff(self, rf):
    langues = LanguageCatalog.objects.create(
        lang_src='wz',
        lang_dest='en',
        percent='4'
    )
    req = rf.get(reverse("dashboard.staff:lang-update", kwargs={'pk': langues.pk}))
    data = {'lang_src': 'it',
            'lang_dest': 'en',
            'percent': '34'}
    req = rf.post(reverse(
        "dashboard.staff:lang-update", kwargs={'pk': langues.pk}), data=data)
    req.user = UserFactory()
    resp = views.LangUpdateView.as_view()(req, pk=langues.pk)

I precise that the MessageMiddleware is present in MIDDLEWARE settings. I use Django==2.0.13.

Emilio Conte
  • 1,105
  • 10
  • 29
  • Check if you have django.contrib.messages in INSTALLED_APPS and django.contrib.messages.middleware.MessageMiddleware in MIDDLEWARE_CLASSES. – cwhisperer Apr 25 '19 at 07:29
  • As said in my post, and unfortunately, it is installed. I did as suggested here https://stackoverflow.com/questions/41033222/django-1-9-unittest-error-for-messages-but-i-dont-see-the-error-for-1-10-4-and-1 with no success. – Emilio Conte Apr 25 '19 at 07:35
  • related: https://stackoverflow.com/q/23861157/ – djvg Jun 02 '23 at 12:10

2 Answers2

14

I found the solution. In order to test a such request, you need first to annotate it with a session and then a message. Actually it means to add these lines:

from django.contrib.messages.middleware import MessageMiddleware
from django.contrib.sessions.middleware import SessionMiddleware

# in your test method:
"""Annotate a request object with a session"""
middleware = SessionMiddleware()
middleware.process_request(req)
req.session.save()

"""Annotate a request object with a messages"""
middleware = MessageMiddleware()
middleware.process_request(req)
req.session.save()

# and then (in my case)
resp = views.LangUpdateView.as_view()(req, pk=langues.pk)
Emilio Conte
  • 1,105
  • 10
  • 29
  • 2
    When you use `RequestFactory`, only the view runs, not the middleware. If you use the [request client](https://docs.djangoproject.com/en/2.2/topics/testing/tools/#default-test-client), then the middleware will run and you won't have to manually annotate the request object. – Alasdair Apr 25 '19 at 10:06
  • I didn't use ```client``` because I didn't manage to setup a ```user``` with ```is_staff``` == True... I definitely should study that point. Thanks ! – Emilio Conte Apr 25 '19 at 10:10
  • Great reply, together with context manager suggested by @Andrzej Bistram, 10/10, thank you! – dotz Aug 20 '21 at 16:26
5

You can also move manual request annotation int a separate context manager that can be reused within multiple tests, the code would look like this then:

import contextlib

from django.contrib.messages.middleware import MessageMiddleware
from django.contrib.sessions.middleware import SessionMiddleware


@contextlib.contextmanager
def middleware(request):
    """Annotate a request object with a session"""
    middleware = SessionMiddleware()
    middleware.process_request(request)
    request.session.save()

    """Annotate a request object with a messages"""
    middleware = MessageMiddleware()
    middleware.process_request(request)
    request.session.save()
    yield request


def test_authenticated_staff(self, rf):
    langues = LanguageCatalog.objects.create(
        lang_src='wz',
        lang_dest='en',
        percent='4'
    )
    req = rf.get(reverse("dashboard.staff:lang-update", kwargs={'pk': langues.pk}))
    data = {'lang_src': 'it',
            'lang_dest': 'en',
            'percent': '34'}
    req = rf.post(reverse("dashboard.staff:lang-update", kwargs={'pk': langues.pk}), data=data)
    req.user = UserFactory()
    with middleware(req):  # << !
        resp = views.LangUpdateView.as_view()(req, pk=langues.pk)