Why don't my Django unittests know that MessageMiddleware is installed?

Viewed 8179

I'm working on a Django project and am writing unittests for it. However, in a test, when I try and log a user in, I get this error:

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

Logging in on the actual site works fine -- and a login message is displayed using the MessageMiddleware.

In my tests, if I do this:

from django.conf import settings
print settings.MIDDLEWARE_CLASSES

Then it outputs this:

('django.middleware.cache.UpdateCacheMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.cache.FetchFromCacheMiddleware',
'debug_toolbar.middleware.DebugToolbarMiddleware')

Which appears to show the MessageMiddleware is installed when tests are run.

Is there an obvious step I'm missing?

UPDATE

After suggestions below, it does look like it's a settings thing.

I currently have settings/__init__.py like this:

try:
    from settings.development import *
except ImportError:
    pass

and settings/defaults.py containing most of the standard settings (including MIDDLEWARE_CLASSES). And then settings.development.py overrides some of those defaults like this:

from defaults import *

DEBUG = True
# etc

It looks like my dev site itself works fine, using the development settings. But although the tests seem to load the settings OK (both defaults and development) settings.DEBUG is set to False. I don't know why, or whether that's the cause of the problem.

9 Answers

In my case (django 1.8) this problem occurs in when unit-test calls signal handler for user_logged_in signal, looks like messages app has not been called, i.e. request._messages is not yet set. This fails:

from django.contrib.auth.signals import user_logged_in
...

@receiver(user_logged_in)
def user_logged_in_handler(sender, user, request, **kwargs):

    ...
    messages.warning(request, "user has logged in")

the same call to messages.warning in normal view function (that is called after) works without any issues.

A workaround I based on one of the suggestions from https://code.djangoproject.com/ticket/17971, use fail_silently argument only in signal handler function, i.e. this solved my problem:

messages.warning(request, "user has logged in",
                 fail_silently=True )

This happened to me in the login_callback signal receiver function when called from a unit test, and the way around the problem was:

from django.contrib.messages.storage import default_storage

@receiver(user_logged_in)
def login_callback(sender, user, request, **kwargs):
    if not hasattr(request, '_messages'):  # fails for tests
        request._messages = default_storage(request)

Django 2.0.x

I found when I had a problem patching messages the solution was to patch the module from within the class under test (obsolete Django version BTW, YMMV). Pseudocode follows.

my_module.py:

from django.contrib import messages


class MyClass:

    def help(self):
        messages.add_message(self.request, messages.ERROR, "Foobar!")

test_my_module.py:

from unittest import patch, MagicMock
from my_module import MyClass


class TestMyClass(TestCase):

    def test_help(self):
        with patch("my_module.messages") as mock_messages:
            mock_messages.add_message = MagicMock()
            MyClass().help()  # shouldn't complain about middleware
Related