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
に設定されています。理由はわかりませんが、それが問題の原因かどうかはわかりません。
Django 1.4 バグがあります RequestFactoryでリクエストを作成する場合。
この問題を解決するには、RequestFactoryを使用してリクエストを作成し、次のようにします。
from Django.contrib.messages.storage.fallback import FallbackStorage
setattr(request, 'session', 'session')
messages = FallbackStorage(request)
setattr(request, '_messages', messages)
私のために働く!
これを非常にエレガントに解決する方法は、 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)
私の場合(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 からの提案の1つに基づく回避策、シグナルハンドラー関数でのみfail_silently
引数を使用します。つまり、これで問題が解決しました。
messages.warning(request, "user has logged in",
fail_silently=True )
これは、単体テストから呼び出されたときに、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
Settings.pyは1つだけですか?
メッセージのパッチ適用で問題が発生したときの解決策は、テスト対象のクラス内からモジュールにパッチを適用することでした(廃止された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
これは、以下にMessagingRequest
ヘルパークラスを作成することにより、 Tarsis Azevedoの回答 に基づいています。
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%
テストはカスタム(テスト)データベースを作成します。たぶん、そこにメッセージなどがないのかもしれません...たぶん、setUp()フィクスチャか何かが必要ですか?
正しく答えるにはもっと情報が必要です。
単純に次のようなことをしてみませんか?確かにデバッグモードでテストを実行しますか?
# settings.py
DEBUG = True
from Django.conf import settings
# where message is sent:
if not settings.DEBUG:
# send your message ...