¿Cómo realizar pruebas unitarias con diferentes configuraciones en Django?


¿Hay algún mecanismo simple para sobreescribir la configuración de Django para una prueba unitaria? Tengo un gestor en uno de mis modelos que devuelve un número específico de los últimos objetos. El número de objetos que devuelve está definido por una configuración NUM_LATEST.

Esto tiene el potencial de hacer que mis pruebas fallen si alguien cambia la configuración. ¿Cómo puedo anular los ajustes en setUp() y restaurarlos posteriormente en tearDown()? Si eso no es posible, ¿hay alguna manera de que pueda patch el método de mono ¿o burlarse de los ajustes?

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

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 usa settings.NEWS_LATEST_MAX para dividir el conjunto de consultas. El getattr() se utiliza simplemente para proporcionar un valor predeterminado en caso de que la configuración no exista.

Author: Soviut, 2009-05-27

8 answers

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

Desde Django 1.4, hay formas de anular la configuración durante las pruebas: https://docs.djangoproject.com/en/dev/topics/testing/tools/#overriding-settings

TestCase tendrá un auto.settings context manager, y también habrá un decorador @override_settings que se puede aplicar a un método de prueba o a un TestCase completo subclase.

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

Si desea cambiar la configuración de todas 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 ambos hspander y dmitrii enfoques.

 139
Author: slinkp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:33

Puedes hacer lo que quieras a la subclase UnitTest, incluyendo la configuración y lectura de las propiedades de la instancia:

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 de django se ejecutan con un solo subproceso, sin embargo, tengo curiosidad sobre qué más puede estar modificando el valor NUM_LATEST? Si ese" algo más " se activa por su rutina de prueba, entonces no estoy seguro de que cualquier cantidad de parches de mono salvará la prueba sin invalidar la veracidad de las pruebas en sí.

 42
Author: Jarret Hardie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-05-27 12:37:56

Actualizar : la solución a continuación solo es necesaria en Django 1.3.x y anteriores. Para >1.4 véase la respuesta de slinkp.

Si cambia la configuración con frecuencia en sus pruebas y usa 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)

Entonces puedes hacer:

with patch_settings(MY_SETTING='my value', OTHER_SETTING='other value'):
    do_my_tests()
 20
Author: akaihola,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:26:05

Aunque sobreescribir la configuración de configuración en tiempo de ejecución podría ayudar, en mi opinión debería crear un archivo separado para las pruebas. Esto ahorra mucha configuración para las pruebas y esto aseguraría que nunca termines haciendo algo irreversible (como limpiar la base de datos de staging).

Diga que su archivo de prueba existe en 'my_project/test_settings.py", añadir

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

En su manage.py. Esto asegurará que cuando ejecute python manage.py test solo utilice test_settings. Si usted está usando algún otro cliente de prueba como pytest, usted podría fácilmente añadir esto a pytest.ini

 11
Author: hspandher,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-01 05:44:42

Puede pasar la opción --settings al ejecutar pruebas

python manage.py test --settings=mysite.settings_local
 8
Author: MicroPyramid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-10 06:32:29

@override_settings es excelente si no tiene muchas diferencias entre las configuraciones de su entorno de producción y de prueba.

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

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

Por lo que necesita tener la mayor parte de su configuración en base.py y luego en otros archivos que necesita para importar todo desde allí, y anular algunas opciones. Así es como se verá su archivo test.py:

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 usted o necesita especificar la opción --settings como en la respuesta @ MicroPyramid, o especificar la variable de entorno DJANGO_SETTINGS_MODULE y luego puede ejecutar sus pruebas:

export DJANGO_SETTINGS_MODULE=settings.test
python manage.py test 
 4
Author: Dmitrii Mikhailov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-27 17:41:26

Encontré esto mientras intentaba arreglar algunos doctests... Para completar, quiero mencionar que si va a modificar la configuración al usar doctests, 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
 3
Author: Jiaaro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-14 15:26:21

Estoy usando pytest.

Logré resolver esto de la siguiente manera:

import django    
import app.setting
import modules.that.use.setting

# do some stuff with default setting
setting.VALUE = "some value"
django.setup()
import importlib
importlib.reload(app.settings)
importlib.reload(modules.that.use.setting)
# do some stuff with settings new value
 1
Author: Brontes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-10 14:02:03