Uno de los momentos más importantes al escribir una aplicación en python es testear (palabra existente en la RAE). Puede ser aburrido, tedioso y {{ Inserta tu insulto favorito aquí }}, pero testear salvará tu vida cuando ese compañero descuidado, que puedes ser tú a las 7:00 a.m. un lunes, vaya y desorganice todo el código justo antes del momento de entrega. También te ayudará con esos bugs complicados que tienden a escabullirse; así que es una buena idea testear todo lo que programes (o, al menos, la mayoría).

Uno de los temas sobre los que no he encontrado información pertinente es cómo escribir tests que implican archivos. En mi caso, y para este post, necesitaba testear imágenes pero lo descrito aquí funcionará para cualquier tipo de archivo, espero. v

Primero lo primero, no llenar el servidor de basura

Casi toda la poca información que encontré sobre el tema de archivos en pruebas implicaba crear un archivo cada vez que el test se corre y luego dejarlo ahí, solo y perdido en alguno de las carpetas del proyecto por el resto de la eternidad (De este tema hablaré luego). Ese comportamiento no era de mi agrado puesto que si mi proyecto implicaba muchas pruebas con archivos, mucha basura sería creada en mi servidor, siendo necesario eliminarla manualmente.

Así que busqué otra solución y la descubrí en la librería de python tempfile. Usando esta librería, junto con override_settings de django.test, serás capaz de crear archivos temporales en tu test, guardarlos en una carpeta temporal y estar seguro que eventualmente tu sistema se encargará de borrarlo. Veamos el proceso paso por paso.

Primero, escribamos un modelo realmente muy básico

[code lang=”python”]
from django.db import models

class Picture(models.Model):
    picture = models.ImageField()
[/code]

Luego, escribamos un test realmente muy básico.

[code lang=”python”]
from PIL import Image
import tempfile
from django.test import TestCase
from .models import Picture
from django.test import override_settings

def get_temporary_image(temp_file):
    size = (200, 200)
    color = (255, 0, 0, 0)
    image = Image.new("RGBA", size, color)
    image.save(temp_file, ‘jpeg’)
    return temp_file

class PictureDummyTest(TestCase):

    @override_settings(MEDIA_ROOT=tempfile.gettempdir())
    def test_dummy_test(self):
            temp_file = tempfile.NamedTemporaryFile()
            test_image = get_temporary_image(temp_file)
            #test_image.seek(0)
            picture = Picture.objects.create(picture=test_image.name)
            print "It Worked!, ", picture.picture
            self.assertEqual(len(Picture.objects.all()), 1)

[/code]

Lo primero que hay que notar es el decorador @override_settings. Como su nombre lo indica, este decorador permite sobrescribir las variables de configuración que serán usadas mientras el test se corre. Para este caso, la variable MEDIA_ROOT, la ruta del sistema donde Django guarda todos los archivos subidos por el usuario. Si esta ruta no es sobrescrita, todas las imagenes creadas durante las pruebas serán guardadas ahí; llenando de basura nuestro servidor.

Para esto, la solución que encontré fue sobrescribir esta ruta con tempfile.gettempdir(); esta función de la librería tempfile retorna el nombre del directorio usado para archivos temporales (retornará /tmp, /var/tmp, /usr/tmp o similar, dependiendo del sistema operativo que estés utilizando). Al ejecutar este paso, todas las imágenes creadas durante las pruebas serán guardadas en el directorio temporal y eventualmente serán borradas por el sistema operativo. De esta manera, me aseguro que no quedará basura en mi servidor cuando las pruebas terminen de correr.

La segunda cosa que hay que notar es tempfile.NamedTemporaryFile(). Esta función de la librería tempfile crea un archivo temporal con un nombre visible en el sistema de archivos (la ruta visible de donde está localizado este archivo). Este archivo temporal es usado luego por la función get_temporary_image para crear un pequeño cuadrado rojo y guardarlo en dicho archivo temporal. Finalmente, con

[code]picture = Picture.objects.create(picture=test_image.name)[/code]

Le estoy diciendo a Django “Hey, crea una instancia del modelo Picture con test_image como su campo picture” Nótese que estoy usando test_image.name para decirle a Django la ruta donde está test_image. Luego de esto, Django tomará la imagen y guardará una copia, relacionada con la instancia del modelo Picture que acabó de crear, en MEDIA_ROOT (que sobreescribimos).

Al correr el test, la respuesta del print es

[code]It Worked!,  /tmp/tmpIp8YS0[/code]

Como pueden ver, la imagen fue guardada en /tmp/ por lo que eventualmente será borrada por el sistema operativo.

Espero que este pequeño tutorial haya sido de utilidad.

Una última cosa antes de terminar. Si necesitan testear imágenes como archivos adjuntos en un HTTP request, solamente necesitan utilizar la imagen creada en los datos del request sin utilizar el nombre de la imagen (.name) ya que están adjuntando la imagen, no su nombre, y utilizar test_image.seek(0) para buscar el primer cuadro; como si la imagen acabara de ser abierta. En este enlace encontrarán más información sobre el proceso, utilizando El módulo de imágenes PIL

[code lang=”python”]      
test_image.seek(0)
response = self.client.put(
self.reverse(‘upload_user_picture’),
    {‘profile_picture’: test_image})
[/code]