| Nota: Este artículo es una traducción (con permiso) de You never know when the brakes might fail, escrito por Damien Tanner y publicado en New Bamboo |
Por si fallan los frenos
Con Rails podemos ir a toda máquina a la hora de desarrollar nuestras aplicaciones, pero con esta velocidad sufrimos un mayor riesgo de descarrilar (no puedo resisitrme a hacer comparaciones ferroviarias). Afortunadamente para nosotros Rails (con todo el cariño de Ruby) tiene un arsenal de pruebas soberbio. Muchos de los que conozco aún no han adoptado la mentalidad del desarrollo orientado a pruebas, lo cual es comprensible porque es una manera totalmente nueva de pensar que lleva cierto tiempo para aprenderla y adaptarse a ella. A principio puede parecer complicado apreciar los méritos de la automatización de pruebas, porque puede significar que tardas el doble de tiempo en construir tu aplicación. Pero algún dia una de tus pruebas fallará, investigarás y descubrirás la causa del fallo y te imaginarás qué habría pasado si ese fallo no hubiera sido descubierto a tiempo. A partir de entonces te darás cuentas, de forma definitiva, de la potencia de las pruebas automáticas.
El uso de pruebas por codigo también te da libertad. Por ejemplo, en un eciente proyecto nuestro hicimos hace un par de semanas ciertos cambios estructurales importantes en la base de datos, y con unos 50 modelos hubiera sido un infierno hacer todo lo que hacía falta sin emplear pruebas unitarias. Una batería completa de tests nos permitió hacer los cambios en la base de datos y ver, inmediatamente, qué modelos de la aplicación necesitaban ser modificados y si las modficiaciones que habíamos hecho eran correctas. En última instancia, las baterias de pruebas me permiten dormir por las noches.
Sorprendentemente, todavía hay pocos tutoriales para ayudarnos a la hora de empezar a escribir nuestras pruebas. Sin embargo, las cosas están cambiando deprisa, por suerte. Esperamos que este artículo te ayuda a familiarizarte con los diferentes tipos de pruebas que se usan con más frecuencia.
Pruebas unitarias
Probar los modelos es vital para que la aplicación funcione. Una batería completa de pruebas untiarias tambien nos da la lbiertad de cambiar los esquemas de la base de datos y la lógica de negocio sin miedo a que se nos cuelen los problemas inadvertidamente. Una vez que le pilles el truco, te preguntarás cómo fué que antes podías vivir sin escribir pruebas unitarias.
Hay diferentes patrones comunes que utilizo a la hora de escribir pruebas unitarias.
Prueba de relación
Es facil cometer errores en las relaciones, especialmente si el esquema de la base de datos evoluciona durante el desarrollo. Cuando uno se encuentra inmerso en un proyecto de envergadura se necesita una bateria completa de pruebas para dormir bien por las noches y por suerte son los mas fáciles de escribir.
Supongamos que en nuestros modelos un Autor tiene muchos Articulos. Esto podría probarse de esta manera:
1 2 3 |
def test_autor assert_equal articulos(:primer_articulo).autor, autores(:damien) end |
Necesitariamos cubrir con pruebas todas las relaciones belongs_to,has_one, has_many y has_and_belongs_to_many en nuestro modelo.
Prueba de creación
En una batería completa de pruebas, nos hará falta tener pruebas de creación, lectura, actualización y borrado. La creación es el sitio donde es mas probable que las cosas vayan mal.
Para comprobar si un registro ha sido creado correctamente, antes nos hacia falta contar el número actual de registros,
almacenarlo, y luego volver a contar y comparar después de la invocación del método create. Sin embargo, ahora tenemos
un método llamado assert_difference y su contrapartida assert_no_diference. Cuando vi estos metodos por primera vez
utilizados en una prueba no me pareció que tuvieran mucho sentido (supongo que era porque por defecto, estos metodos
comprueban una diferencia de 1 sin que haga falta que se lo especifiques explícitamente), sin embargo una lectura
rápida del código me ayudó a entender el funcionamiento:
1 2 3 4 5 6 7 8 9 10 |
def assert_difference(object, method = nil, difference = 1) initial_value = object.send(method) yield assert_equal initial_value + difference, object.send(method) end def assert_no_difference(object, method, &block) assert_difference object, method, 0, &block end |
Así, ahora podemos escribir pruebas de creación de objetos de esta guisa:
1 2 3 4 5 |
def test_create assert_difference Articulo, :count do autores(:damien).articulos.create(:titulo => 'Segundo articulo', :cuerpo => '...') end end |
Lo cual es lo mismo que hacer:
1 2 3 4 5 |
def test_create cuenta_inicial = Articulo.count autores(:damien).articles.create(:titulo => 'Segundo articulo', :cuerpo => '...') assert_equal cuenta_inicial + 1, Articulo.count end |
Prueba de validación
Después de que ya tengamos nuestras pruebas de creación, querremos extender las validaciones en el modelo. En lugar
de montar todos los parámetros a create() en cada prueba, nos será útil añadir un metodo para crear el registro
y permitir sobreescribir los parámetros:
1 2 3 4 5 6 7 |
protected def create_articulo(opciones = {}) attrs = { :titulo => 'Mi segundo articulo', :contenido => '...', :contenido_extendido=> 'mas texto' }.merge(opciones) Articulo.create(attrs) end |
Así, para validar la presencia o no de atributos de obligada presencia, podriamos hacer:
1 2 3 4 5 6 |
def test_validates_presence_of_titulo assert_no_difference Articulo, :count do nuevo_articulo = create_article({ :titulo => nil }) assert new_articulo.errors.on(:titulo) end end |
Obsérvese que ademas de comprobar que no hay diferencia en el número de articulos en la base de datos (lo cual nos dice
que el articulo mal creado no ha sido almacenado en la base de datos), también nos hemos asegurado que se ha asignado
un error al atributo titulo. Tambien se podría hacer esta comprobación fuera del bloque assert_difference, pero
creo que queda mejor dentro.
Pruebas de acceso
Es posible saltarse las pruebas de lectura de registors, pero si hemos definidos métodos personalizados para leer atributos es posible que queramos añadir pruebas para ellos.
Por ejemplo, en nuestro modelo Articulo hemos añadido un método resumen que devuelve una copia truncada de los
contenidos del articulo. Para probarlo, haríamos así:
1 2 3 |
def test_excerpt assert_equal articulos(:mi_primer_post).resumen.length, 100 end |
Este es un ejemplo muy simple, pero una vez que empecemos a tratar con la lógica de negocio (y más aun con el dinero de la gente) estaremos obligados a probar los métodos de un modelo.
Prueba de borrado
He aquí mi método favorito para probar la destruccion de registros:
1 2 3 4 5 |
def test_delete assert_difference Articulo, :count, -1 do Articulo.destroy(articulos(:mi_primer_post)) end end |
Pruebas funcionales
Como desarrollador, todos sabemos lo importantes que son nuestros modelos, pero poca gente realmente llega a verlos. A lo
que realmente prestan atencino nuestors usuarios es a los controladores y las vistas. Si no estamos escribiendo pruebas
funcionales probablement nuestros clientes o usuarios están acostumbrados a ver de vez en cuando los errores de tipo
ApplicationError. Sin embargo hay esperanza. Si escribimos una bateria completa de pruebas funcionales, estos errores
de aplicación serán cosa del pasado.
Prueba de éxito
La prueba mas básica que nos ahorrara sufrir errores de aplicación será la prueba de éxito. Lo que hacemos es requerir una acción del controlador y asegurarnos que la respuesta HTTP es OK. Y ya que estamos podemos asegurarnos que de que se muestra la vista correcta y que están bien construidas las variables que hacen falta en la vista:
1 2 3 4 5 6 |
def test_show get :show, :id => 1 assert_response :success assert_template 'show' assert assigns(:articulo) end |
Con esta prueba nos aseguramos que visitar /articulos/show/1 mostrará la página utilizando la plantilla articulos/show.rhtml y que la variable @@articulo tendrá un valor que podrá ser usado por la vista.
Prueba de redirección
Las acciones que se invoquen en el área de usuarios o de administración de nuestra aplicación deberían redirigirnos a otra página si no estamos registrados. Para comprobar que lo hacemos bien podremos emplear assert_redirected_to
1 2 3 4 5 |
def test_destroy_requires_admin_priv get :destroy, :id => 1 assert_equal 'Solo administradores', flash[:notice] assert_redirected_to '/admin/login' end |
En este caso tambien nos hemos asegurado de que mostramos el mensaje adecuado.
Las pruebas CRUD
Igual que con las pruebas unitarias de los modelos, querremos comprobar las acciones de creación, acceso, actualización y borrado (CRUD, de Create, Read, Update, Delete). De nuevo utilizaremos assert_difference:
1 2 3 4 5 6 |
def test_destroy assert_difference Articulo, :count do login_as_admin get :destroy, :id => 1 end end |
Tambien querremos comprobar el envio de formularios como cuando se crean articulos nuevos en un blog:
1 2 3 4 5 6 |
def test_create assert_difference Articulo, :count do login_as_admin post :new, { :titulo => 'Post de prueba', :cuerpo => 'Texto de artículo' } end end |
Pruebas de etiquetas
Si pudiésemos probar las etiquetas de marcado HTML que mostraremos al usuario, estaremos probando exactamente lo que el usuario verá. Siempre se pueden probar los títulos importantes y los enlaces entre páginas.
Uno de los metodos más útiles en las pruebas funcionales es assert_tag, que buscará una etiqueta específica en el marcado HTML devuelto por la aplicación. Han aparecido recientmente dos alternativas a este metodo: Hpricot y sus métodos de pruebas (también hay disponible un plugin) y assert_select que también permite usar selectores CSS para buscar etiquetas concretas. No cabe duda de que estos dos plugins prometen mucho, peor por ahora considero que assert_tag es adecuado en la mayoría de las ocasiones.
En este ejemplo, nos aseguraremos que la página de listado de todos los articulos tiene los títulos correctos con enlaces al pagína de mostrar cada artículo:
1 2 3 4 5 6 7 8 |
def test_titulos_listado get :list articulos.each do |articulo| assert_tag :tag => 'a', :attributes => { :href => "/articles/show/#{articulo.id}" }, :child => { :tag => 'h2', :content => articulo.titulo } end end |
Una lectura rápida de la documentación de assert_tag nos ayudará a entender mejor su uso.
Aún no las tengo todas conmigo
Uno de los libros mas útiles que tengo es Practices of an Agile Developer. Si alguna vez tengo mis dudas o vacilo en algún proyecto particular, abro el libro y leo algunos apartados. Todas las practicas son concisas y le levantarán el ánimo incluso al desarrollador mas ignorante. Algunas de las prácticas hacen referencias al desarrollo guiado por pruebas y las herramientas asociadas.


Sorry, comments are closed for this article.