2009-05-27 17 views
82

¿Hay algún mecanismo simple para anular la configuración de Django para una prueba unitaria? Tengo un administrador en uno de mis modelos que devuelve un número específico de los últimos objetos. La cantidad de objetos que devuelve se define mediante una configuración NUM_LATEST.Cómo probar la unidad con diferentes configuraciones en Django?

Esto tiene el potencial de hacer que mis pruebas fallen si alguien cambiara la configuración. ¿Cómo puedo anular las configuraciones en setUp() y luego restaurarlas en tearDown()? Si eso no es posible, ¿hay alguna forma de que yo pueda parchear el método o simular la configuración?

EDIT: Aquí está mi código de gestor:

class LatestManager(models.Manager): 
    """ 
    Returns a specific number of the most recent public Articles as defined by 
    the NEWS_LATEST_MAX setting. 
    """ 
    def get_query_set(self): 
     num_latest = getattr(settings, 'NEWS_NUM_LATEST', 10) 
     return super(LatestManager, self).get_query_set().filter(is_public=True)[:num_latest] 

El administrador utiliza para cortar settings.NEWS_LATEST_MAX queryset. El getattr() se usa simplemente para proporcionar un valor predeterminado si la configuración no existe.

+0

@Anto: ¿puedes explicar por qué o dar una mejor respuesta? – user

+0

Cambió mientras tanto; el primero aceptado fue [este] (http://stackoverflow.com/a/913596/1030960);) – Anto

Respuesta

134

EDIT: Esta respuesta se aplica si desea para cambiar la configuración de un pequeño número de pruebas específicas.

Desde Django 1.4, hay maneras de redefinir los valores durante las pruebas: https://docs.djangoproject.com/en/dev/topics/testing/tools/#overriding-settings

TestCase tendrá un gestor de contexto self.settings, y también habrá un decorador de @override_settings que se puede aplicar a cualquiera de una prueba método o una subclase de TestCase completa.

Estas características aún no existían en Django 1.3.

Si desea cambiar la configuración de todos sus pruebas, querrá crear un archivo de configuración separado para la prueba, que puede cargar y anular la configuración de su archivo de configuración principal.Hay varios buenos enfoques para esto en las otras respuestas; He visto variaciones exitosas en los enfoques hspander's y dmitrii's.

+4

Diría que esta es la mejor manera de hacer esto ahora en Django 1.4+ –

+0

¿Cómo accedes luego a esa configuración desde las pruebas? Lo mejor que he encontrado es algo como 'self.settings(). Wrapped.MEDIA_ROOT', pero eso es bastante terrible. – mlissner

+2

Las versiones más nuevas de Django tienen un administrador de contexto específico para esto: https://docs.djangoproject.com/en/1.8/topics/testing/tools/#overriding-settings – Akhorus

41

Usted puede hacer lo que guste a la subclase UnitTest, incluyendo propiedades de la instancia de ajuste y la lectura:

from django.conf import settings 

class MyTest(unittest.TestCase): 
    def setUp(self): 
     self.old_setting = settings.NUM_LATEST 
     settings.NUM_LATEST = 5 # value tested against in the TestCase 

    def tearDown(self): 
     settings.NUM_LATEST = self.old_setting 

Dado que los casos de prueba Django corren un único subproceso, sin embargo, tengo curiosidad por saber qué otra cosa puede ser ¿modificando el valor NUM_LATEST? Si su rutina de prueba desencadena esa "otra cosa", entonces no estoy seguro de que la cantidad de parches de mono guarde la prueba sin invalidar la veracidad de las pruebas.

+0

Agregué un ejemplo de código. – Soviut

+0

Ah, tengo ... el ejemplo del código lo aclara. –

+0

Su ejemplo funcionó. Esto ha sido una revelación en términos del alcance de las pruebas unitarias y cómo la configuración en el archivo de prueba se propaga a través de la pila de llamadas. – Soviut

3

encontrado este al intentar solucionar algunos prueba unitaria ... Para completar quiero mencionar que si usted va a modificar la configuración de prueba unitaria cuando se utiliza, debe hacerlo antes de importar cualquier otra cosa ...

>>> from django.conf import settings 

>>> settings.SOME_SETTING = 20 

>>> # Your other imports 
>>> from django.core.paginator import Paginator 
>>> # etc 
19

Actualización: la solución a continuación solo es necesaria en Django 1.3.xy versiones anteriores. Para> 1.4 ver slinkp's answer.

Si cambia la configuración con frecuencia en sus pruebas y el uso de Python ≥2.5, esto también es útil:

from contextlib import contextmanager 

class SettingDoesNotExist: 
    pass 

@contextmanager 
def patch_settings(**kwargs): 
    from django.conf import settings 
    old_settings = [] 
    for key, new_value in kwargs.items(): 
     old_value = getattr(settings, key, SettingDoesNotExist) 
     old_settings.append((key, old_value)) 
     setattr(settings, key, new_value) 
    yield 
    for key, old_value in old_settings: 
     if old_value is SettingDoesNotExist: 
      delattr(settings, key) 
     else: 
      setattr(settings, key, old_value) 

A continuación, puede hacer:

with patch_settings(MY_SETTING='my value', OTHER_SETTING='other value'): 
    do_my_tests() 
+0

Esta es una solución genial Por alguna razón, mi configuración no funcionaba correctamente en las pruebas de la unidad. Solución muy elegante, gracias por compartir. – Tomas

+0

Estoy usando este código, pero tuve problemas con las fallas de prueba en cascada, porque la configuración no se revierte si la prueba en cuestión falla.Para abordar esto, agregué un try/finally alrededor de la declaración 'yield', con la parte final de la función contenida en el bloque' finally', por lo que las configuraciones siempre se revierten. –

+0

Editaré la respuesta para la posteridad. ¡Espero estar haciendo esto bien! :) –

9

Si bien la configuración de las configuraciones generales en el tiempo de ejecución puede ayudar, en mi opinión, debe crear un archivo separado para la prueba. Esto ahorra mucha configuración para las pruebas y esto garantizaría que nunca termine haciendo algo irreversible (como limpiar la base de datos de etapas).

Di existe en el archivo de prueba 'mi_proyecto/test_settings.py', añadir

settings = 'my_project.test_settings' if 'test' in sys.argv else 'my_project.settings' 

en su manage.py. Esto garantizará que cuando ejecute python manage.py test solo use test_settings. Si está utilizando algún otro cliente de prueba como PYtest, podría tan fácilmente agrega esto a pytest.ini

7

Puede pasar --settings opción cuando la ejecución de pruebas

python manage.py test --settings=mysite.settings_local 
+0

se detuvo para encontrar aplicaciones que se encuentran en settings.dev que es la extensión de settings.base – holms

3

@override_settings es grande si usted no tiene muchos diferencias entre su producción y las configuraciones del entorno de prueba.

En otro caso, será mejor que tenga diferentes archivos de configuración. En este caso el proyecto se verá así:

your_project 
    your_app 
     ... 
    settings 
     __init__.py 
     base.py 
     dev.py 
     test.py 
     production.py 
    manage.py 

Así que hay que tener su mayor parte de sus ajustes en base.py y luego en otros archivos que necesita para importar todos todo, desde allí, y anular algunas opciones. Esto es lo que su archivo test.py se verá así:

from .base import * 

DEBUG = False 

DATABASES = { 
    'default': { 
     'ENGINE': 'django.db.backends.sqlite3', 
     'NAME': 'app_db_test' 
    } 
} 

PASSWORD_HASHERS = (
    'django.contrib.auth.hashers.MD5PasswordHasher', 
) 

LOGGING = {} 

Y entonces tampoco necesita especificar --settings opción como en respuesta @MicroPyramid, o especificar DJANGO_SETTINGS_MODULE variable de entorno y entonces se puede ejecutar las pruebas:

export DJANGO_SETTINGS_MODULE=settings.test 
python manage.py test 
Cuestiones relacionadas