Pruebas de Unidad

Preparando un Ambiente de Prueba

Como vimos cuando estabamos configurando la base de datos, usted necesita tener una base de datos separada para prueba. Si usted ha seguido el tutorial y no creó una base de datos de prueba, la puede crear ahora (vacía) simplemente ejecutando el comando CREATE DATABASE direcciones_test; (o el nombre que tenga su base de datos).

Probando el Directorio

Ahora sí estamos listos para crear las pruebas de nuestro directorio. Rails ya añadió algunas pruebas a nuestro sistema esqueleto, así que intentaremos ejecutarlas:

$ rake
(in /home/david/play/direcciones)
/ruby/bin/ruby -Ilib;test "/ruby/lib/ruby/gems/1.8/gems/rake-0.6.2/lib/rake/rake_test_loader.rb" 
"test/unit/persona_test.rb"
Loaded suite /ruby/lib/ruby/gems/1.8/gems/rake-0.6.2/lib/rake/rake_test_loader
Started
.
Finished in 0.2 seconds.

1 tests, 1 assertions, 0 failures, 0 errors
/ruby/bin/ruby -Ilib;test "/ruby/lib/ruby/gems/1.8/gems/rake-0.6.2/lib/rake/rake_test_loader.rb" 
"test/functional/personas_controller_test.rb"
Loaded suite /ruby/lib/ruby/gems/1.8/gems/rake-0.6.2/lib/rake/rake_test_loader
Started
........
Finished in 0.751 seconds.

8 tests, 28 assertions, 0 failures, 0 errors

¡Qué bueno, pasamos! ¿Pero qué se probó? Veamos las pruebas que tenemos. Los programas en el directorio test son:

$ find test -name *.rb
test/functional/personas_controller_test.rb
test/test_helper.rb
test/unit/persona_test.rb

Pruebas de Unidad vs Funcionales

En el esqueleto creado por rails, las pruebas se dividen en pruebas de unidad (que idealmente prueban cada método de nuestro programa), y pruebas funcionales, que prueban que su programa haga lo que usted espera.

En general, las pruebas funcionales prueban el controlador, y las pruebas de unidad prueban el modelo y las clases de soporte

Cuando usted creó el módulo MVC de personas, el generador de código preparó esqueletos para sus pruebas de unidad.

Para ver de qué se compone una prueba básica, veamos el archivo test/unit/persona_test.rb:

require File.dirname(__FILE__) + '/../test_helper'

class PersonaTest < Test::Unit::TestCase
  fixtures :personas

  # Replace this with your real tests.
  def test_truth
    assert_kind_of Persona, personas(:first)
  end
end

Una prueba de unidad en ruby es una clase que hereda de Test::Unit::TestCase. Dentro de esta clase nosotros escribimos las pruebas.

Adornos (Fixtures)

A veces nuestras pruebas de unidad requieren de registros de prueba en la base de datos. En terminología rails, estos registros se llaman "fixtures" o adornos. Se llaman así porque son útiles primariamente para que la prueba tenga datos con los cuales trabajar.

Los adornos para nuestra tabla se encuentran en el folder test/fixtures/personas.yml. Como podrá imaginar, cuando creamos nuesto módulo utilizando el generador rails nos hizo el favor de incluir este archivo de adorno para la tabla:

# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
first:
  id: 1
another:
  id: 2

Lo que tenemos que hacer ahora es añadir nuestros datos iniciales. El llenado de los adornos es muy sencillo, y se hace de acuerdo a la descripción de la tabla que diseñamos con anterioridad:

# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
personaje_novela:
  id: 1
  nombre: Alonso
	apellido_paterno: Quijano
	apellido_materno: Cervantes
	desplegar: Alonso Quijano
	sexo: Hombre
	notas: Nacio en un lugar de la Mancha de cuyo nombre no quiero acordarme.

actor_famoso:
  id: 2
  nombre: Pedro
	apellido_paterno: Infante
	apellido_materno: Cruz
	desplegar: Pedro Infante
	sexo: Hombre
	notas: El Idolo de Guamuchil

Note que en el formato YML podemos nombrar nuestros registros para describir su naturaleza, que nos ayudará para que nuestras pruebas sean más claras. En este caso, cambiamos los nombres "first" y "another" por "personaje_novela" y "actor_famoso". Como cambiamos el nombre de nuestro primer registro, nuestro método "test_truth" en el listado anterior también necesita cambiar.

Utilizando Adornos

Estas pruebas son un poco triviales, pero hagamos un par de pruebas de unidad para comprobar que esto funcione:

def test_actor_famoso
  actorazo = personas(:actor_famoso)
  assert_equal "Infante", actorazo.apellido_paterno
end

Desarrollo Basado en Pruebas

Con anterioridad vimos brevemente que Ruby soporta Desarrollo basado en Pruebas, pero no explicamos específicamte la mecánica del desarrollo basado en pruebas con un ejemplo. Ahora veremos un ejemplo práctico de éste desarrollo, que le permitirá comprender el proceso de pensamiento que utiliza un desarrollador que se basa en pruebas.

Cambio de Metodología

Lo que vamos a presentar a continuación representa un cambio de metodología y tren de pensamiento de lo que muchos desarrolladores están acostumbrados.

Acostumbrarse a probar primero no es sencillo, pero vale la pena intentarlo.

Apellidos en Español

En la lengua de Cervantes, los apellidos tienen reglas un poco especiales. Necesitamos implementar un método llamado apellidos que desplegará los apellidos de la siguiente manera:

Como estamos desarrollando basados en nuestras pruebas, primero tenemos que preparar una prueba que falle, y datos de prueba:

Preparando Datos de Prueba

Para los datos de prueba, añadimos a personas.yml registros que cubran los casos adecuados:

sin_appellidos:
  id: 3
  nombre: Maria
  desplegar: Maria
  sexo: Mujer

solo_apellido_paterno:
  id: 4
  nombre: Juan
  apellido_paterno: Gomez

solo_apellido_materno:
  id: 5
  nombre: Pedro
  apellido_materno: Paramo
	
ambos_apellidos:
  id: 6
  nombre: Juan
  apellido_materno: Torres
  apellido_paterno: Mendoza

Código de Prueba

Ahora que tenemos información de prueba, podemos crear nuestras pruebas. Todavía no tenemos el código, pero sabemos lo que esperamos en los diferentes casos. Así que añadimos a nuestro archivo test/unit/persona_test.rb los métodos de prueba que necesitamos. Comenzamos los métodos con test para que la clase TestCase los ejecute:

class PersonaTest < Test::Unit::TestCase
  fixtures :personas

  def test_ambos_apellidos
    bien_nacido = personas(:ambos_apellidos)
    assert_equal( bien_nacido.apellido_paterno+' '+bien_nacido.apellido_materno, bien_nacido.apellidos )

  end

  def test_solo_paterno
    persona = personas(:solo_apellido_paterno)
    assert_equal( persona.apellido_paterno, persona.apellidos )
  end

  def test_solo_materno
    persona = personas(:solo_apellido_materno)
    assert_equal( persona.apellido_materno, persona.apellidos )
  end

  def test_sin_apellidos
    persona = personas(:sin_apellidos)
    assert_equal( "", persona.apellidos )
  end



end

Ahora podemos escribir un metodo "cabo" (un método básicamente vacío), que ataremos con el código adecuado más tarde. En nuestro archivo app/models/persona.rb.

def apellidos
  ""
end

La Prueba que Falla

Ahora cuando ejecutemos rake, veremos que la única prueba que funciona es la de sin_apellidos. Funciona de pura casualidad, porque la implementación de nuestro método está vacia:


$ rake TEST=test/unit/persona_test.rb
(in /cygdrive/c/home/david/play/direcciones)
/usr/bin/ruby -Ilib:test 
  "/usr/lib/ruby/gems/1.8/gems/rake-0.7.0/lib/rake/rake_test_loader.rb" 
	"test/unit/persona_test.rb"
Loaded suite /usr/lib/ruby/gems/1.8/gems/rake-0.7.0/lib/rake/rake_test_loader
Started
F.FF.
Finished in 0.611 seconds.

  1) Failure:
test_ambos_apellidos(PersonaTest) [./test/unit/persona_test.rb:13]:
<"Mendoza Torres"> expected but was
<"">.

  2) Failure:
test_solo_materno(PersonaTest) [./test/unit/persona_test.rb:24]:
<nil> expected but was
<"">.

  3) Failure:
test_solo_paterno(PersonaTest) [./test/unit/persona_test.rb:19]:
<"Gomez"> expected but was
<"">

5 tests, 5 assertions, 3 failures, 0 errors
rake aborted!
Test failures

(See full trace by running task with --trace)


Note que podemos ejecutar sólo una prueba simplemente añadiendo el nombre del archivo ruby que contiene su prueba.

Pero lo importante que hay que tener en cuenta es que, gracias a nuestras pruebas de unidad (que fueron creadas antes del código basadas en el diseño), tenemos las siguientes ventajas:

Implementando la función

Finalmente, podemos implementar la función para los apellidos, y continuar arreglando problemas hasta que nuestro comando de prueba muestre el resultado deseado...


$ rake TEST=test/unit/persona_test.rb
... 
5 tests, 5 assertions, 0 failures, 0 errors

Y ahora si podemos decir que nuestro soporte de apellidos en español está completo, y tenemos cinco pruebas que lo demuestran.