¿Por qué mis pruebas de unidad Django no saben que MessageMiddleware está instalado?
unit-testing messages (7)
¿Solo tienes un settings.py?
Estoy trabajando en un proyecto de Django y estoy escribiendo pruebas de unidad para él. Sin embargo, en una prueba, cuando intento registrar un usuario, obtengo este error:
MessageFailure: You cannot add messages without installing django.contrib.messages.middleware.MessageMiddleware
Iniciar sesión en el sitio real funciona bien, y se muestra un mensaje de inicio de sesión utilizando el MessageMiddleware.
En mis pruebas, si hago esto:
from django.conf import settings
print settings.MIDDLEWARE_CLASSES
Luego produce esto:
(''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'')
Lo cual parece mostrar que el MessageMiddleware está instalado cuando se ejecutan las pruebas.
¿Hay un paso obvio que me falta?
ACTUALIZAR
Después de las sugerencias a continuación, parece que es una cosa de configuración.
Actualmente tengo la settings/__init__.py
así:
try:
from settings.development import *
except ImportError:
pass
y settings/defaults.py
contienen la mayoría de las configuraciones estándar (incluyendo MIDDLEWARE_CLASSES
). Y luego settings.development.py
anula algunos de los valores predeterminados como este:
from defaults import *
DEBUG = True
# etc
Parece que mi sitio de desarrollo funciona bien, usando la configuración de desarrollo. Pero a pesar de que las pruebas parecen cargar la configuración de OK (ambos valores predeterminados y el desarrollo), la settings.DEBUG
se establece en False
. No sé por qué, o si esa es la causa del problema.
Django 1.4 tiene un error al crear la solicitud con RequestFactory.
Para resolver este problema, cree su solicitud con RequestFactory y haga esto:
from django.contrib.messages.storage.fallback import FallbackStorage
setattr(request, ''session'', ''session'')
messages = FallbackStorage(request)
setattr(request, ''_messages'', messages)
¡Funciona para mi!
En mi caso (django 1.8), este problema se produce cuando la unidad de prueba llama al controlador de señal para la señal user_logged_in
, parece que la aplicación de mensajes no se ha llamado, es decir, request._messages
todavía no está establecida. Esto falla:
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")
la misma llamada a messages.warning
en la función de vista normal (que se llama después) funciona sin problemas.
Una solución alternativa basada en una de las sugerencias de https://code.djangoproject.com/ticket/17971 , use el argumento fail_silently
solo en la función del manejador de señal, es decir, esto resolvió mi problema:
messages.warning(request, "user has logged in",
fail_silently=True )
Esto se basa en share al crear a continuación una clase auxiliar de MessagingRequest
.
Dado que diga KittenAdmin
me gustaría obtener una cobertura de prueba del 100% para:
from django.contrib import admin, messages
class KittenAdmin(admin.ModelAdmin):
def warm_fuzzy_method(self, request):
messages.warning(request, ''Can I haz cheezburger?'')
test_helpers.py
una clase de ayuda de MessagingRequest
para utilizar, por ejemplo, en un archivo 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()]
Luego, en un estándar 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)
Resultados:
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%
Las pruebas crean una base de datos personalizada (pruebas). Tal vez no tengas mensajes allí o algo ... ¿Tal vez necesites accesorios setUp () o algo así?
Necesita más información para responder correctamente.
¿Por qué no simplemente hacer algo como? ¿Seguro que ejecutas pruebas en modo de depuración, verdad?
# settings.py
DEBUG = True
from django.conf import settings
# where message is sent:
if not settings.DEBUG:
# send your message ...
Si ve un problema en su Middleware, entonces no está haciendo "Prueba de Unidad". Las pruebas unitarias prueban una unidad de funcionalidad. Si interactúa con otras partes de su sistema, está haciendo algo llamado prueba de "integración".
Deberías intentar escribir mejores pruebas, y este tipo de problemas no deberían surgir. Pruebe 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)
Una forma de resolver esto bastante elegante es burlarse del módulo de mensajes utilizando mock
Digamos que tiene una vista basada en clase llamada FooView
en la aplicación llamada 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/'')
...
Ahora puedes probarlo con
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)