Factory Boy

Hoy he empezado a utilizar una nueva herramienta para un proyecto de Django en el que estoy trabajando: Factory Boy.

La descripción del proyecto lo dice bastante claro: es una herramienta para ayudar en la creación de fixtures para tests, es decir, conjuntos de datos de prueba sobre los que correr tests de código automáticos.

En un proyecto de código real, es fácil que nos encontramos dos cosas:

1. Tendremos modelos con un montón de campos, bastantes de ellos obligatorios.

2. El volumen de relaciones entre distintos modelos puede ser alto. Por ejemplo, un libro puede tener una FK hacia un autor, y el autor a su vez una FK hacia su país de nacimiento, etc.

¿A qué nos lleva esto? Que para poder probar cualquier cosa en nuestros tests automáticos necesitamos un conjunto relativamente grande y pesado de objetos de prueba (la fixture) sobre los que correr la mayoría de los tests. Para solucionar este problema, lo que yo normalmente hacía era crear una fixture básica principal sobre la que corría todos los tests luego.

Esto tiene el inconveniente básico de que todos los tests que usen esa fixture tendrán que recrear cada vez esos objetos (porque Django hace un necesario flush de las BBDD antes de cada nuevo test), lo que lleva mucho tiempo.

Uno puede pensar en montar una jerarquía de fixtures para que cada conjunto de tests sólo cree (más o menos) los objetos que necesite. En mi opinión, esto puede ser una pesadilla de mantener y, por lo tanto, una razón más para acabar no escribiendo tests automáticos, por lo que yo no lo recomiendo.

Aquí es donde entra Factory Boy, que a mi juicio tiene básicamente dos ventajas muy importantes:

1. Permite poner valores por defecto (incluso dinámicos) para los distintos campos del modelo, de forma que al crearlo sólo hay que especificar los valores que no queremos que sean los por defecto.

Esto puede parecer una tontería, pero si se tienen muchos campos obligatorios es una bendición. Especialmente cuando se añaden campos nuevos a los modelos: en vez de tener que cambiarlo en todas partes, basta con cambiarlo en la definición de la factoría y punto.

2. Permite crear objetos en cadena. Es decir, si el modelo libro tiene una FK a un autor obligatoria, se pueden especificar los atributos deseados del libro y el autor se creará solo con los valores por defecto que hayamos especificado.

Aquí es donde se ahorra también un montón de tiempo, ya que podemos crear fácilmente los objetos que se necesitarán en cada test al inicio de la función del propio test, en vez de hacer una función setUp compartida para todos los tests que cree objetos que no necesitan todos los tests.

En resumen, una herramienta muy útil que recomiendo a cualquiera que haga TDD con Python, y especialmente con Django.