Revista Informática

Ruby on Rails desde Cero: Pruebas unitarias (Testing)

Publicado el 15 noviembre 2013 por Codehero @codeheroblog

¿Por qué usar pruebas para nuestras aplicaciones?

Esta pregunta quizás todos los nuevos desarrolladores se la cuestionen a la hora de programar. Diseñar nuestra aplicación web en base a pruebas nos da muchísimos beneficios, ya que evalúan los métodos que el desarrollador considere de importancia, evaluando si el funcionamiento es correcto.

Imaginemos un ejemplo simple: Si tenemos un método dentro de algunos de nuestros modelos, que reciba dos valores, los sume y retorne una respuesta, pudiéramos hacer una prueba unitaria que pruebe el método con dos valores predeterminados de los cuales conozcamos de antemano su respuesta. Algo así:

test "suma_simple" do assert_equal( suma(2,2), 4 ) end

123 test"suma_simple"do  assert_equal(suma(2,2),4)end

En esta pequeña prueba se verifica que el resultado de la suma sea igual a cuatro, si esto es así, la prueba resultará exitosa.

En Rails resulta bastante fácil el desarrollo de pruebas, ya que usamos la consola para crear nuestros controladores, modelos o helpers que se van creando dentro de la carpeta test ordenados por tipo.

Otro motivo para no temerle a las pruebas es la fácil ejecución de ellas. Sólo basta con una línea de comando y tendremos un diagnóstico completo de las pruebas satisfactorias y así asegurar que el código se adhiere a la funcionalidad deseada, incluso después de algunas modificaciones de nuestra aplicación.

En las pruebas de Rails podemos realizar todo tipo de validaciones que nos indiquen si está funcionando o no correctamente, incluso se pueden simular peticiones del navegador y por lo tanto, usted puede probar la respuesta de su aplicación sin tener que hacerlo a través del navegador.


Preparación de las pruebas.

Antes de desarrollar nuestras pruebas, debemos conocer una serie de componentes importantes que interactúan con nuestra pruebas.

Entorno

Debemos saber que todas las aplicaciones que desarrollamos en Rails tienen por defecto tres entornos: Entorno de desarrollo, entorno de producción, y entorno para pruebas. Esto significa que cada uno de ellos es completamente aislado del resto, lo que nos da la confianza de realizar todas las pruebas que queramos sin alterar nuestro ambiente de producción o de desarrollo.

Estructura

Como ya mencionamos en la primera parte de este tutorial, la estructura de las pruebas prácticamente está alojada en una carpeta definida por Rails llamada test y básicamente esta es la estructura:

  • controllers/
  • helpers/
  • test_helper.rb
  • fixtures/
  • integration/
  • models/

Una estructura bastante sencilla de explicarlo es: models se hacen las pruebas de nuestros modelos de la aplicación; controllers las pruebas de los controladores; integration está destinado para contener las pruebas que involucren integración con nuestra aplicación; fixtures es una forma de organizar nuestros datos de prueba y por último test_helper.rb es un archivo que contiene la configuración por defecto para sus análisis.

Datos

Los datos que vamos a utilizar de pruebas se guardan en la carpeta fixtures y nos sirven para ser usados en las pruebas. De estos archivos, existe uno para cada modelo que tenemos en la aplicación y son independientes de la base de datos. La estructura del contenido de estos archivos es más o menos así:

# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html one: nombre: Ricardo apellido: Sampayo nacimiento: 1988-05-09 sexo: m two: nombre: CodeHero apellido: Cursos nacimiento: 2013-09-05 sexo: m

12345678910111213 # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html one:  nombre:Ricardo  apellido:Sampayo  nacimiento:1988-05-09  sexo:m two:  nombre:CodeHero  apellido:Cursos  nacimiento:2013-09-05  sexo:m

Se puede observar fácilmente en la estructura que se crean dos objetos de tipo usuario.


¿Cómo ejecutar pruebas?

Ejecutar pruebas en Rails es bastante sencillo sólo debemos ejecutar unas líneas de comando y ver qué resultados nos arroja. Primero debemos asegurarnos de que la base de datos de prueba esté creada con éxito, si no debemos ejecutar los comandos ya estudiados para generarla. (Recuerde que la configuración de las bases de datos está en el archivo database.yml).

$ rake db:migrate ... $ rake db:test:load

123 $rake db:migrate$rake db:test:load

Algunos de los comandos para preparar la aplicación de pruebas son:

  • rake db:test:clone crea una base de datos de pruebas basado en el actual.
  • rake db:test:clone_structure Crea nuevamente la estructura de la base de datos de prueba según la base de datos de desarrollo.
  • rake db:test:load Crea la base de datos de prueba con el esquema schema.rb.
  • rake db:test:prepare Comprueba migraciones y cargar el esquema de prueba.
  • rake db:test:purge Vacía la base de datos.

Listo! una vez comprobado y preparado nuestro ambiente de pruebas sólo nos queda saber cómo ejecutar pruebas. Sólo debemos ejecutar la siguiente límea de comando para llamar a todas las pruebas del sistema.

rake test

1 rake test

Y obtendremos algo así:

Run options: --seed 20850 # Running tests: . Finished tests in 0.094405s, 10.5927 tests/s, 10.5927 assertions/s. 1 tests, 1 assertions, 0 failures, 0 errors, 0 skips

123456789 Run options:--seed20850 # Running tests:  Finished tests in0.094405s,10.5927tests/s,10.5927assertions/s 1tests,1assertions,0failures,0errors,0skips

También podemos llamar a los test de forma independiente, con el nombre de la prueba:

rake test test/models/usuario_test.rb

1 rake test test/models/usuario_testrb

O incluso más detallada con el nombre del método dentro de la prueba.

rake test test/models/usuario_test.rb test_prueba_codehero

1 rake test test/models/usuario_testrb test_prueba_codehero

Para este caso forzamos una prueba para que falle y tenemos esto como resultado:

Run options: -n test_prueba_codehero --seed 51120 # Running tests: F Finished tests in 0.056565s, 17.6788 tests/s, 17.6788 assertions/s. 1) Failure: UsuarioTest#test_prueba_codehero [/Users/ricardosampayo/Documents/CodeHero/ruby_on_rails_activerecord/test/models/usuario_test.rb:6]: Failed assertion, no message given. 1 tests, 1 assertions, 1 failures, 0 errors, 0 skips

12345678910111213 Run options:-ntest_prueba_codehero--seed51120 # Running tests: F Finished tests in0.056565s,17.6788tests/s,17.6788assertions/s   1)Failure:UsuarioTest#test_prueba_codehero [/Users/ricardosampayo/Documents/CodeHero/ruby_on_rails_activerecord/test/models/usuario_test.rb:6]:Failed assertion,no message given 1tests,1assertions,1failures,0errors,0skips

Como vemos, el resultado de las pruebas es bastante completo, nos indica tanto la cantidad de pruebas que se ejecutaron, como el resultado de ellas indicándonos fallas o errores, incluso vemos cuales pruebas son las que tienen problemas y el rendimiento de la ejecución.

TDD (Desarrollo basado en pruebas) es uno de los enfoques de desarrollo del software más importantes hasta el momento, éste consiste en primero pensar y desarrollar una prueba y luego desarrollamos la aplicación basada ésta.

Es una buena práctica tener al menos una prueba para cada una de las validaciones y por lo menos una prueba por cada método en el modelo.

Ejemplo

Vamos a ejecutar una serie de pruebas variadas para mostrar un poco cómo funcionan las pruebas.

En esta prueba desarrollaremos tres métodos que actúan sobre el objeto Usuario y validan que se pueda guardar un usuario, el nombre de un usuario sea el correcto, entre otras.

require 'test_helper' class UsuarioTest < ActiveSupport::TestCase test "guardar_usuario" do user1 = Usuario.new({ apellido: 'Sampayo', nombre: 'Ricardo', nacimiento: Time.zone.parse("1988-05-09"), sexo: 'm' }) assert user1.save end test "validar_nombre" do user1 = Usuario.new({ apellido: 'Sampayo', nombre: 'Ricardo', nacimiento: Time.zone.parse("1988-05-09"), sexo: 'm' }) assert_equal( user1.nombre, 'Ricardo') end test "prueba_fallida" do assert false end end

12345678910111213141516171819 require'test_helper' classUsuarioTest&lt;ActiveSupport::TestCase    test"guardar_usuario"do  user1=Usuarionew({apellido:'Sampayo',nombre:'Ricardo',nacimiento:Timezoneparse("1988-05-09"),sexo:'m'})  assertuser1save  end   test"validar_nombre"do  user1=Usuarionew({apellido:'Sampayo',nombre:'Ricardo',nacimiento:Timezoneparse("1988-05-09"),sexo:'m'})  assert_equal(user1nombre,'Ricardo')    end   test"prueba_fallida"do  assertfalse  end end

El resultado al ejecutar estas pruebas es el siguiente:

Run options: --seed 43885 # Running tests: .F. Finished tests in 0.076813s, 39.0559 tests/s, 39.0559 assertions/s. 1) Failure: UsuarioTest#test_prueba_fallida [/Users/ricardosampayo/Documents/CodeHero/ruby_on_rails_activerecord/test/models/usuario_test.rb:16]: Failed assertion, no message given. 3 tests, 3 assertions, 1 failures, 0 errors, 0 skips

12345678910111213 Run options:--seed43885 # Running tests: F Finished tests in0.076813s,39.0559tests/s,39.0559assertions/s   1)Failure:UsuarioTest#test_prueba_fallida [/Users/ricardosampayo/Documents/CodeHero/ruby_on_rails_activerecord/test/models/usuario_test.rb:16]:Failed assertion,no message given 3tests,3assertions,1failures,0errors,0skips

Probablemente se hayan dado cuenta de que hay varios tipos de afirmaciones para las pruebas, veremos unas cuantas a continuación:

  • assert (test, [MSG]) verifica si la prueba es verdadera.
  • assert_not (test, [MSG]) verifica si la prueba es falsa.
  • assert_equal (expected, actual, [MSG]) asegura que dos variables son iguales.
  • assert_not_equal (expected, actual, [MSG]) asegura que dos variables son diferentes.
  • assert_nil (obj, [MSG]) asegura que el objeto es nulo.
  • assert_not_nil (obj, [MSG]) asegura que el objeto no es nulo.
  • assert_match (regexp, cadena, [MSG]) asegura que la respuesta coincida con una expresión regular.
  • assert_no_match (regexp, cadena, [MSG]) asegura que la respuesta no coincida con una expresión regular.
  • assert_kind_of (clase, obj, [MSG]) asegura que el objeto coincida con una clase.
  • assert_not_kind_of (regexp, cadena, [MSG]) asegura que el objeto no coincida con una clase.

Conclusión

En esta lección hemos estudiado básicamente las pruebas unitarias en Ruby on Rails, mostrando la definición, características importantes e importancia de desarrollar framework utilizando pruebas y algunos ejemplos básicos.

Una vez más te recomiendo echarle un vistazo a la serie completa de Ruby on Rails desde cero, así como a las otras series de CodeHero, agradeciendo de antemano todas sus dudas y comentarios en la sección de comentarios.

¡Hasta el próximo capítulo!


Volver a la Portada de Logo Paperblog