40

我正在开发一个 Django 项目,并正在为它编写单元测试。但是,在测试中,当我尝试登录用户时,出现此错误:

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

在实际站点上登录工作正常——并且使用 MessageMiddleware 显示登录消息。

在我的测试中,如果我这样做:

from django.conf import settings
print settings.MIDDLEWARE_CLASSES

然后它输出这个:

('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')

这似乎表明在运行测试时安装了 MessageMiddleware。

我错过了一个明显的步骤吗?

更新

在下面的建议之后,它看起来确实是一个设置的东西。

我目前有settings/__init__.py这样的:

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

settings/defaults.py包含大部分标准设置(包括MIDDLEWARE_CLASSES)。然后settings.development.py像这样覆盖其中一些默认值:

from defaults import *

DEBUG = True
# etc

使用开发设置,看起来我的开发站点本身运行良好。但是,尽管测试似乎加载了 OK(默认值和开发)settings.DEBUG设置为False. 我不知道为什么,或者这是否是问题的原因。

4

9 回答 9

66

当您使用 RequestFactory 创建可能触发此错误的请求时,Django 1.4具有预期的行为。

要解决此问题,请使用 RequestFactory 创建您的请求并执行以下操作:

from django.contrib.messages.storage.fallback import FallbackStorage
setattr(request, 'session', 'session')
messages = FallbackStorage(request)
setattr(request, '_messages', messages)

为我工作!

于 2012-08-17T19:18:17.190 回答
4

解决这个问题的一种非常优雅的方法是使用mock来模拟消息模块

假设您有一个名为FooViewapp 的基于类的视图,名为myapp

from django.contrib import messages
from django.views.generic import TemplateView

class FooView(TemplateView):
    def post(self, request, *args, **kwargs):
        ...
        messages.add_message(request, messages.SUCCESS, '\o/ Profit \o/')
        ...

您现在可以使用

def test_successful_post(self):
    mock_messages = patch('myapp.views.FooView.messages').start()
    mock_messages.SUCCESS = success = 'super duper'
    request = self.rf.post('/', {})
    view = FooView.as_view()
    response = view(request)
    msg = _(u'\o/ Profit \o/')
    mock_messages.add_message.assert_called_with(request, success, msg)
于 2013-02-01T12:31:53.797 回答
1

在我的情况下(django 1.8),当单元测试为user_logged_in信号调用信号处理程序时会出现此问题,看起来消息应用程序尚未被调用,即request._messages尚未设置。这失败了:

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

messages.warning对普通视图函数(之后调用)的相同调用没有任何问题。

我基于https://code.djangoproject.com/ticket/17971的建议之一的解决方法,仅在信号处理程序函数中使用fail_silently参数,即这解决了我的问题:

messages.warning(request, "user has logged in",
                 fail_silently=True )
于 2018-02-08T13:55:25.370 回答
0

你只有一个settings.py吗?

于 2012-08-13T16:23:32.087 回答
0

测试创建自定义(测试)数据库。也许您那里没有消息或其他东西......也许您需要 setUp() 固定装置或其他东西?

需要更多信息才能正确回答。

为什么不简单地做类似的事情?你确定在调试模式下运行测试对吗?

# settings.py
DEBUG = True

from django.conf import settings
# where message is sent:
if not settings.DEBUG:
    # send your message ... 
于 2012-08-13T17:24:45.103 回答
0

这建立在Tarsis Azevedo 的答案之上,在下面创建了一个MessagingRequest帮助程序类。

假设KittenAdmin我想获得 100% 的测试覆盖率:

from django.contrib import admin, messages

class KittenAdmin(admin.ModelAdmin):
    def warm_fuzzy_method(self, request):
        messages.warning(request, 'Can I haz cheezburger?')

我创建了一个MessagingRequest辅助类来使用一个test_helpers.py文件:

from django.contrib.messages.storage.fallback import FallbackStorage
from django.http import HttpRequest

class MessagingRequest(HttpRequest):
    session = 'session'

    def __init__(self):
        super(MessagingRequest, self).__init__()
        self._messages = FallbackStorage(self)

    def get_messages(self):
        return getattr(self._messages, '_queued_messages')

    def get_message_strings(self):
        return [str(m) for m in self.get_messages()]

然后在标准 Django 中tests.py

from django.contrib.admin.sites import AdminSite
from django.test import TestCase

from cats.kitten.admin import KittenAdmin
from cats.kitten.models import Kitten
from cats.kitten.test_helpers import MessagingRequest

class KittenAdminTest(TestCase):
    def test_kitten_admin_message(self):
        admin = KittenAdmin(model=Kitten, admin_site=AdminSite())
        expect = ['Can I haz cheezburger?']
        request = MessagingRequest()
        admin.warm_fuzzy_method(request)
        self.assertEqual(request.get_message_strings(), expect)

结果:

coverage run --include='cats/kitten/*' manage.py test; coverage report -m
Creating test database for alias 'default'...
.
----------------------------------------------------------------------
Ran 1 test in 0.001s

OK
Destroying test database for alias 'default'...
Name                                     Stmts   Miss  Cover   Missing
----------------------------------------------------------------------
cats/kitten/__init__.py                      0      0   100%   
cats/kitten/admin.py                         4      0   100%   
cats/kitten/migrations/0001_initial.py       5      0   100%   
cats/kitten/migrations/__init__.py           0      0   100%   
cats/kitten/models.py                        3      0   100%   
cats/kitten/test_helpers.py                 11      0   100%   
cats/kitten/tests.py                        12      0   100%   
----------------------------------------------------------------------
TOTAL                                       35      0   100%   
于 2016-04-21T03:33:41.227 回答
0

当我从单元测试中调用 login_callback 信号接收器函数时,这发生在我身上,解决问题的方法是:

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

于 2018-09-15T20:37:35.717 回答
0

我发现当我在修补消息时遇到问题时,解决方案是从被测类(过时的 Django 版本 BTW,YMMV)中修补模块。伪代码如下。

我的模块.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
于 2019-07-09T11:16:59.907 回答
-1

如果您在中间件中发现问题,那么您不是在进行“单元测试”。单元测试测试一个功能单元。如果您与系统的其他部分进行交互,那么您就是在进行所谓的“集成”测试。

你应该尝试编写更好的测试,这样的问题不应该出现。尝试RequestFactory。;)

def test_some_view(self):
    factory = RequestFactory()
    user = get_mock_user()
    request = factory.get("/my/view")
    request.user = user
    response = my_view(request)
    self.asssertEqual(status_code, 200)
于 2012-08-13T17:34:30.257 回答