SecNot

feb 15, 2014

Tests más rápidos en Django con SQLite

Esta semana he estado desarrollando una webapp django en un ordenador sin SSD, y el tiempo de ejecución era mucho mayor de lo normal, ya no recordaba lo lentos que son los discos duros especialmente su acceso aleatorio. Por supuesto me he puesto a investigar formas de acelerar el proceso.

Cuando estás desarrollando una aplicación django tienes tres opciones como base de datos MySQL, PostgreSQL, o SQLite. A la hora de hacer tests SQLite es indiscutiblemente la más rápida, ya que crea la base de datos en ram, pero no tiene soporte completo de ALTER TABLE, por lo que no es posible hacer migraciones. MySQL y PostgreSQL en cambio son lentas en la creación de bases de datos, pero soportan migraciones.

La solución perfecta es usar MySQL o PostgreSQL para el desarrollo, y usar SQLite en los tests. Esto lo podemos conseguir modificando el archivo de configuración cuando se van a ejecutar tests, para cambiar la base de datos a sqlite3.

Primero creamos un archivo de configuración alternativo test_settings.py, en el añadimos la configuración de la base de datos a usar en los tests:

# test_settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3', #
        'NAME': 'test_sqlitedb', # Ruta al archivo de la base de datos
    }
}

Tras esto solo queda modificar settings.py para que cargue el archivo de configuración cuando esté ejecutando tests, esto lo conseguimos comprobando si los argumentos de la linea de comandos contiene la palabra "test". Al final del archivo settings.py hay que añadir:

# ^^^^^^^^ Resto de configuracion settings.py ^^^^^^
# Cargar configuracion test_settings.py si se ejecuta manage.py test
import sys
if 'test' in sys.argv:
    try:
        from test_settings import *
    except ImportError:
        print "No se pudo encontrar el archivo test_settings.py"

El tiempo de ejecución de la aplicación usando PostgreSQL:

$ time python manage.py test nombre_app
Creating test database for alias 'default'...
...............
----------------------------------------------------------------------
Ran 15 tests in 1.047s

OK
Destroying test database for alias 'default'...

real    0m14.169s
user    0m1.204s
sys     0m0.136s

Y usando SQLite:

$ time python manage.py test nombre_app
Creating test database for alias 'default'...
...............
----------------------------------------------------------------------
Ran 15 tests in 0.299s

OK
Destroying test database for alias 'default'...

real    0m1.031s
user    0m0.872s
sys     0m0.100s

Una reducción del 90% (13 segundos), nada mal para una modificación tan sencilla.