Acelerando las pruebas de Django

Estoy buscando aprender más sobre sus flujos de prueba con Django.

Información de fondo http://docs.djangoproject.com/en/dev/topics/testing/

Estoy encontrando dificultades al usar el desarrollo guiado por pruebas. El corredor de prueba de Django crea constantemente todos los modelos de db en un db de prueba al iniciar. Para nuestros proyectos actuales (entre 40 y 240 modelos), esto significa que se necesitan fácilmente 20 años para que comiencen las pruebas.

Esto lo hace completamente inviable para probar una nueva característica a menudo. Mi pregunta, ¿cómo trabajan ustedes alrededor de esto?

He intentado algunas cosas en el pasado a.): Cambie el cargador de prueba para reutilizar la misma base de datos de prueba cada vez y aplique migraciones cuando sea necesario b.) Ejecute mis pruebas unitarias desde el flujo de __main__ de archivos de python

la opción b es incómoda con el sys.path, la opción a es factible pero no parece ser la forma de django.

Actualización: la opción A no es una solución tan mala. Es solo un poco de esfuerzo. Lo que me hace creer que la gente usa una solución diferente. SQL Lite podría ser esa solución. Pero supongo que hay más.

cambie el cargador de prueba para reutilizar la misma base de datos de prueba cada vez y aplique migraciones cuando sea necesario

  1. No veo nada incorrecto al escribir su propio corredor de prueba que simplemente trunca las tablas en lugar de eliminar y crear la base de datos. Esto es djangoic porque resuelve un problema específico. Hay un ticket abierto para permitir agrupar casos de prueba en suites de prueba. Una vez que se solucione, debería poder agrupar sus casos de prueba en suites para una administración más fácil. También puede inspeccionar el parche adjunto al boleto y ver si se ajusta a su propósito.

  2. Como sugirió Ned, puede usar una base de datos en memoria. Esto depende en gran medida de su modelo de datos y de las consultas que se pueden transportar a través de bases de datos.

  3. Si aún no ha intentado reorganizar sus casos de prueba. En mi experiencia, no todas las clases de prueba necesitan django.test.TestCase . Averigüe las clases de prueba que pueden hacer con subclasificación unittest.TestCase . Esto acelerará un poco las cosas.

  4. Reorganizar los accesorios. Mueva los accesorios comunes a un solo archivo y cárguelo antes de la ejecución de la prueba en lugar de dentro de cada clase de prueba (utilizando los fixtures = [...] ).

El uso de una base de datos SQLite en memoria durante las pruebas definitivamente acelera las cosas.

No me gusta la idea de usar una base de datos diferente (SQLite) para las pruebas, por lo que mis pruebas unitarias usan la misma base de datos que la aplicación de producción – postgres.

Fuera de la caja, esto hace que crear / destruir la base de datos sea el paso más lento para ejecutar pruebas.

Django 1.8 solucionará este problema con la bandera –keepdb

Pero todavía no estamos allí, por lo que debemos conformarnos con otros medios.

Solución 1) Usa pytest-django

Puede usar eso para hacer que sus pruebas se ejecuten sin volver a crear la base de datos. Funciona. Si solo te importa ejecutar pruebas en la línea de comandos, te sugiero esto.

En mi caso, me gusta usar el IDE de PyCharm, y ser capaz de ejecutar pruebas haciendo clic derecho en archivos / métodos es definitivamente una ventaja para mí, así que tuve que ir con …

Solución 2) El truco TEST_MIRROR.

En su archivo settings.py , configure su base de datos como:

 if os.getenv('USE_TEST_DB') == '1': DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': 'mydbtesting', 'USER': 'mydb', 'PASSWORD': 'mydb', 'HOST': 'localhost', 'PORT': '5432', 'TEST_MIRROR': 'default', } } else: DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': 'mydb', 'USER': 'mydb', 'PASSWORD': 'mydb', 'HOST': 'localhost', 'PORT': '5432', } } 

Entonces, “mydb” es la base de datos que se usará para la ejecución normal y “mydbtesting” es para las pruebas.

La configuración de TEST_MIRROR no está realmente diseñada para esto, pero el hecho es que si ejecuta pruebas con una base de datos configurada de esa manera, Django no volverá a crear / destruir, que es lo que queremos.

Pero primero tenemos que crear esa base de datos con algo como:

 export USE_TEST_DB=1 ./manage.py syncdb --migrate 

Luego, cuando quiera ejecutar pruebas rápidamente, simplemente configure la variable de entorno USE_TEST_DB en ‘1’. Para obtener el mismo beneficio en Pycharm, puede ir a Ejecutar / Depurar configuraciones, Pruebas predeterminadas / Django, luego en Variables de entorno, agregar USE_TEST_DB = 1


ACTUALIZAR:

La aplicación de muestra está en Github: https://github.com/freedomsponsors/www.freedomsponsors.org/blob/099ec1a7a1c404eba287d4c93d58c8cf600b2769

Solo puede ejecutar pruebas que le interesen específicamente, consulte aquí: http://docs.djangoproject.com/en/dev/topics/testing/?from=olddocs#running-tests

Como en este ejemplo, ejecute sólo TestCase specyfic:

 $ ./manage.py test animals.AnimalTest 

En cuanto a la base de datos de prueba, se crea y destruye cada vez que se ejecuta la prueba 🙁 También para probar, puede usar la base de datos sqlite si es posible en su flujo de trabajo.

He encontrado otra forma de acelerar las pruebas. Si sus modelos de prueba son usuarios de autenticación (modelo de User ), y establece una contraseña para ellos, la función de hashing requiere una cantidad decente de milisegundos para finalizar. Lo que hago es agregar esto a mi configuración de prueba:

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

Esto impone el hash MD5 para la contraseña, que es mucho más rápido que el predeterminado. En mi caso, esto mejoró 12 pruebas, cada una crea 7 usuarios, de 4.5 segundos a 500 ms.

¡Tenga cuidado de no agregar esto a su configuración de producción!

He encontrado otra forma de acelerar las pruebas. La operación que consume más tiempo es escribir en / leer desde el disco duro (estoy usando sqlite para las pruebas). La solución es crear ramdisk y colocar el archivo de base de datos sqlite allí. He reducido el tiempo de prueba en un factor de 10.

Creando el ramdisk:

 #!/bin/sh mkdir -p /tmp/ramdisk; chmod 777 /tmp/ramdisk mount -t tmpfs -o size=256M tmpfs /tmp/ramdisk/ 

Cambiando la ruta del archivo db:

 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': '/tmp/ramdisk/test.db', 'TEST_NAME': '/tmp/ramdisk/test.db', } } 

A partir de Django 1.8, puede mantener la base de datos de prueba alrededor para que no la reconstruya cada vez que realice la prueba. Simplemente añada la bandera –keepdb.

 python manage.py test --keepdb 

Cuando tenga nuevas migraciones, excluya el indicador –keepdb y la base de datos de prueba se creará desde cero.

Aquí hay herramientas de prueba simples que proporcionan una base de datos sin recarga junto con señales para que no tenga que preocuparse por la base de datos de prueba https://github.com/plus500s/django-test-tools