Подтвердить что ты не робот

Почему мои Django unittests не знают, что установлено MessageMiddleware?

Я работаю над проектом Django и пишу для него unittests. Тем не менее, в тесте, когда я пытаюсь войти в систему, я получаю эту ошибку:

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 устанавливается при запуске тестов.

Есть ли очевидный шаг, который мне не хватает?

UPDATE

После предложений ниже, это похоже на настройку.

В настоящее время у меня есть 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. Я не знаю, почему, или это причина проблемы.

4b9b3361

Ответ 1

Django 1.4 имеет ошибку при создании запроса с помощью RequestFactory.

Чтобы устранить эту проблему, создайте свой запрос с помощью RequestFactory и выполните следующее:

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

Работает для меня!

Ответ 2

Способом решения этого довольно элегантного является издевка модуля сообщений с помощью 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)

Ответ 3

В моем случае (django 1.8) эта проблема возникает, когда модульное тестирование вызывает обработчик сигнала для сигнала user_logged_in, похоже, что приложение сообщений не было request._messages, то есть 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 )

Ответ 4

У вас есть только один параметр settings.py?

Ответ 5

Тесты создают пользовательскую (тестовую) базу данных. Может быть, у вас нет сообщений там или что-то еще... Может быть, вам нужны приборы setUp() или что-то еще?

Требуется больше информации для правильного ответа.

Почему бы просто не сделать что-то подобное? Вы уверены, что правильно запускаете тесты в режиме отладки?

# settings.py
DEBUG = True

from django.conf import settings
# where message is sent:
if not settings.DEBUG:
    # send your message ... 

Ответ 6

Это основывается на ответе 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%   

Ответ 7

Это произошло со мной в функции приемника сигнала 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)

Джанго 2.0.x

Ответ 8

Я обнаружил, что когда у меня возникла проблема с исправлением сообщений, было решено исправить модуль из тестируемого класса (устаревшая версия 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

Ответ 9

Если вы видите проблему в своем промежуточном ПО, то вы не выполняете "Unit Test". Модульные тесты проверяют функциональность. Если вы взаимодействуете с другими частями вашей системы, вы делаете что-то, называемое "интеграционным" тестированием.

Вы должны попытаться написать лучшие тесты, и такие проблемы не должны возникать. Попробуйте 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)