Contenido

Django: Test del modelo y Fixtures

Una de las herramientas más potentes para la creación de pruebas en Django son las fixtures.

Las fixtures consisten en datos que se cargan en la base de datos para poder realizar pruebas.

Para mostrarlo, continuaré con el post de creación de un sitio básico con django, donde construimos un blog básico.

Django

Requisitos

En principio, con el blog básico ya construido es suficiente. Nos vendrá bien la herramienta de administración, con el fin de automatizar un poco el proceso.

Si queremos utilizar YAML para los datos, es muy probable que necesitemos tener instalado el módulo de YAML para Python (en Debian, python-yaml).

En principio nos conformaremos con XML y JSON.

Metiendo datos

Voy a suponer que tenemos una base de datos limpia. Si no es así, no os preocupéis: la única diferencia es que vosotros tendréis más datos.

Arrancamos el servicio:

1
2
3
4
5
6
7
$ python manage.py runserver
Validating models...

0 errors found
Django version 1.3, using settings 'myblog.settings'
Development server is running at https://127.0.0.1:8000/
Quit the server with CONTROL-C.

Nos vamos a la interfaz de administración: https://localhost:8000/admin/blog/post/ e insertamos dos Posts. Los míos tienen lo siguiente:

1
2
3
4
5
post1.title = primer post
post1.body = Éste es el cuerpo de mi primer post

post2.title = Segundo Post
post2.body = Éste es el cuerpo de mi segundo post

Inciso: una mejora a la interfaz

Gracias a esto me acabo de dar cuenta de un error en nuestro modelo. En la ventana de Posts aparecerá algo como “Post object”, imposibilitando la identificación de nuestros posts. Esto se debe a que Django no sabe cómo representar nuestros objetos “Post”. Así que vamos a decírselo.

Abrimos nuestro blog/models.py y, en la clase Post, añadimos los métodos __unicode__ y __str__. Como es poquito, os lo pongo todo junto:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=200)
    body = models.TextField()

    def __unicode___(self):
        return self.title

    def __str__(self):
        return self.title

Una vez hecho esto, si refrescamos, deberían aparecernos los títulos de los posts en lugar de las cadenas genéricas.

Tests

Podemos ejecutar los tests de nuestra aplicación:

1
2
3
4
5
6
7
8
$ python manage.py test
Creating test database for alias 'default'...
...................................................................................................................................................................................................................................................................................................................................
----------------------------------------------------------------------
Ran 323 tests in 3.125s

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

Pero… ¡¡ si aún no los hemos escrito !!

Eso no es del todo cierto. Como dependemos del módulo django-admin, se han lanzado los tests de este módulo. Además, se ha comprobado que se puede crear la BBDD. Y, por si fuera poco, Django ya nos creó un archivo blog/tests.py con una prueba (aunque sólo es una plantilla para nuestros tests).

Fixtures por defecto

Cuando sincronizamos la base de datos de la aplicación, siempre se tratan de cargar los fixtures iniciales. Por eso nos aparece la frase: No fixtures found.. Si queremos que se cargen cosas, basta con crear la carpeta blog/fixtures y, dentro de ésta, el archivo blog/fixtures/initial_data.json.

Este archivo es peligroso, ya que se tratará de cargar SIEMPRE que sincronicemos, lo que puede producir duplicación en los datos.

Creando nuestras fixtures

Como no es lo que queremos, obtener un archivo de fixtures sólo para las pruebas. Django nos deja hacerlo fácilmente:

1
$ python manage.py dumpdata blog > blog/fixtures/posts.json

Si miramos nuestro archivo blog/fixtures/posts.json, veremos que contiene los datos de los blogs que creamos, en formato JSON . Si queremos usar el formato XML, basta con decirlo:

1
$ python manage.py dumpdata --format=xml blog > blog/fixtures/posts.xml

Añadiendo las fixtures a los tests

Ya tenemos el modelo, una plantilla para los tests y los datos en formato json. ¡¡Automaticemos el proceso!! (archivo blog/tests.py):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
from django.test import TestCase
from models import Post

class Post1Test(TestCase):
    def test_numero_elementos(self):
        """
        Comprobamos que NO se han cargado las fixtures
        """
        self.assertEqual(0, len(Post.objects.all()))

class Post2Test(TestCase):
    fixtures = ['posts.json']

    def test_numero_elementos(self):
        """
        Comprobamos que se han cargado las fixtures
        """
        self.assertEqual(2, len(Post.objects.all()))

Como véis, he creado dos clases. En la segunda he añadido el atributo de clase “fixtures”, que es un iterable con el nombre de nuestro archivo (sin ruta). La primera clase no tiene ésto.

Por esa razón, django no carga ningún dato en la primera clase pero sí en la segunda; esto se debe a que la base de datos se resetea para cada TestCase.

Sí, pero… yo prefiero XML

Basta con que el archivo tenga extensión .xml y django ya sabe lo que tiene que hacer.

Teóricamente se puede usar YAML, pero no lo he conseguido.

Mi archivo JSON

Aquí tenéis mi archivo json, algo formateado para que quede más bonito (blog/fixtures/posts.json):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[
  {"pk": 1,
   "model": "blog.post",
   "fields": {"body": "\u00c9ste es el cuerpo de mi primer post",
              "title": "primer post"}},
  {"pk": 2, "model":
   "blog.post",
   "fields": {"body": "\u00c9ste es el cuerpo de mi segundo post",
              "title": "Segundo Post"}}
]

Más información

Pues la verdad es que he encontrado muy poco. Referiros a la propia documentación de Django.