我正在研究一个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
使用开发设置看起来我的开发网站本身运行正常。但是,虽然测试似乎加载设置正常(默认值和开发)settings.DEBUG
设置为False
。我不知道为什么,或者这是否是问题的原因。
答案 0 :(得分:58)
Django 1.4 has a bug。
要解决此问题,请使用RequestFactory创建请求并执行以下操作:
from django.contrib.messages.storage.fallback import FallbackStorage
setattr(request, 'session', 'session')
messages = FallbackStorage(request)
setattr(request, '_messages', messages)
适合我!
答案 1 :(得分:4)
解决这个问题非常优雅的方法是使用mock
模拟消息模块假设您在名为FooView
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)
答案 2 :(得分:1)
在我的情况下(django 1.8),当单元测试调用user_logged_in
信号的信号处理程序时出现此问题,看起来消息app尚未被调用,即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 )
答案 3 :(得分:0)
你只有一个settings.py吗?
答案 4 :(得分:0)
测试创建自定义(测试)数据库。也许你没有任何消息或某些东西......也许你需要setUp()灯具或其他东西?
需要更多信息才能正确回答。
为什么不简单地做一些像?你确定在调试模式下运行测试吗?
# settings.py
DEBUG = True
from django.conf import settings
# where message is sent:
if not settings.DEBUG:
# send your message ...
答案 5 :(得分:0)
这建立在Tarsis Azevedo's answer之上,方法是在下面创建一个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%
答案 6 :(得分: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
答案 7 :(得分:0)
我发现在修补消息时遇到问题时,解决方案是从被测类(过时的Django版本BTW,YMMV)中修补模块。伪代码如下。
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
答案 8 :(得分:-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)