efigarolam

efigarolam

Publicado
Mayo 16, 2013

Próximos Eventos

Blog

Validación y testeo de Modelos con ActiveRecord y RSpec.

En este ejemplo muy sencillo vamos a aprender como cambiar el entorno de pruebas de Rails para usar RSpec, así como validar un modelo y escribir las pruebas necesarias para garantizar que dicho modelo funcione correctamente.

Para la elaboración de este ejemplo utilizaremos el enfoque TDD (Test Driven Development) donde primero se escriben las pruebas,luego debemos verificar que fallen, posteriormente escribir solo el código necesario para que pasen y finalmente refactorizar.

Llevaremos a cabo una simple y sencilla aplicación de agenda telefónica. Comencemos creando la aplicación con rails:

rails new PhonebookApp -T

Con el comando anterior, creamos una aplicación llamada PhonebookApp y la opción -T indica que no se debe de crear el directorio de test por default. Rails utiliza TestUnit para las pruebas por default. Nosotros usaremos RSpec por lo que dicho directorio no es necesario para este ejemplo.

Ahora es necesario entrar al directorio de nuestra aplicación desde la consola.

cd PhonebookApp

El siguiente paso es configurar el entorno de pruebas con RSpec para ello es necesario, abrir el archivo Gemfile, ubicado en la raiz de nuestra aplicación y añadir lo siguiente:

group :test, :development do
  gem "rspec-rails", "~> 2.0"
end

Hasta aquí ya tenemos configurado nuestro Gemfile para que incluya la gema "rspec-rails" y todas sus dependencias en los entornos de pruebas y desarrollos de nuestra aplicación. Ahora es necesario ejecutar el siguiente comando para instalar dichas gemas:

bundle

Continuando, debemos inicializar RSpec para ello ejecutamos el siguiente comando en la consola, lo que nos creará el directorio spec y algunos archivos necesarios para la ejecución de pruebas con RSpec:

rails generate rspec:install

Ahora crearemos el modelo, en una agenda teléfonica nos interesa tener los campos de nombre, apellido, teléfono, dirección y la edad. Los campos obligatorios serán nombre y teléfono. El modelo se genera utilizando el siguiente comando:

rails generate model phone_book name:string last_name:string phone:string address:string age:integer

A partir del modelo creado, necesitamos correr las migraciones, esto significa: crear las tablas en la base de datos. Para ello debemos ejecutar el siguiente comando:

rake db:migrate

Siguiendo el enfoque TDD debemos primero escribir las pruebas, para este ejemplo, las pruebas son: debe ser válido con todos los atributos, debe ser inválido sin nombre, debe ser inválido sin teléfono, debe ser válido solo con nombre y teléfono, edad debe ser válida con un número, edad debe ser inválida sin un número. Son 6 pruebas en total, para escribirlas, es necesario hacerlo en el archivo phonebookspec.rb generado automáticamente cuando generamos el modelo, en el directorio de spec/models.

require 'spec_helper'

describe PhoneBook do
  before {
    @valid_attrs = { name: "Juan", last_name: "Perez", phone: "999-99-9-99-99", address: "Col. Vista Hermosa", age: 10 }
  }

  context "model validations" do
    it "is valid with all the attributes" do
      phone_book = PhoneBook.new @valid_attrs

      phone_book.valid?.should be_true
    end

    it "is invalid without name" do
      @valid_attrs.delete(:name)
      phone_book = PhoneBook.new @valid_attrs

      phone_book.valid?.should be_false
      phone_book.errors[:name].size.should > 0
    end

    it "is invalid without phone" do
      @valid_attrs.delete(:phone)
      phone_book = PhoneBook.new @valid_attrs

      phone_book.valid?.should be_false
      phone_book.errors[:phone].size.should > 0
    end

    it "is valid only with name and phone" do 
      @valid_attrs.delete(:last_name)
      @valid_attrs.delete(:address)
      phone_book = PhoneBook.new @valid_attrs

      phone_book.valid?.should be_true
    end

    it "age is valid with a number" do
      phone_book = PhoneBook.new @valid_attrs
      phone_book.age = 10

      phone_book.valid?.should be_true
    end

    it "age is invalid without a number" do 
      phone_book = PhoneBook.new @valid_attrs
      phone_book.age = "Diez"

      phone_book.valid?.should be_false
      phone_book.errors[:age].size.should > 0
    end
  end
end

En el código anterior se muestran las pruebas ya escritas y funcionando, a continuación explicaré el código paso a paso:

  • En la primer línea cargamos RSpec y sus métodos.
  • describe PhoneBook do, indica que vamos a escribir pruebas para la clase del modelo PhoneBook.
  • En el bloque before creamos un hash que contiene los atributos válidos para crear una instancia del modelo PhoneBook.
  • Después definimos el contexto, que es lo que estamos probando, pues las validaciones del modelo.
  • Dentro del contexto están las pruebas individuales, cada bloque de it "description" do
  • En general, creamos instancias de PhoneBook pasandole el hash creado en el before, y para probar las diferentes validaciones, pues quitamos los elementos del hash pertinentes para probar cada escenario.
  • Por ejemplo, en la primer prueba "debe ser válido con todos los atributos" pasamos el hash @valid_attrs tal cual. Para la segunda prueba "debe ser inválido sin nombre" eliminamos el elemento name del hash e instanciamos el modelo, después preguntamos si es válido y esperamos que la respuesta sea falsa. Caso similar con las demás pruebas.

Para ejecutar las pruebas y ver los resultados es necesario ejecutar el siguiente comando desde la consola:

rake

En este punto, nos arrojará 3 pruebas fallidas y 3 falsos positivos (pruebas que indican ser correctas, cuando en realidad no lo son) que dejarán de serlo una vez que escribamos el código necesario para hacer pasar las pruebas.

¿Qué es necesario para hacer pasar las pruebas? Escribir las validaciones pertinentes en el modelo, para ello, haremos uso de los métodos que nos brinda la clase ActiveRecord, clase que todos los modelos heredan automáticamente, el siguiente código deberá ser añadido al archivo phone_book.rb dentro de app/models:

validates :name, presence: true
validates :phone, presence: true
validates :age, numericality: true, allow_blank: true
  • La primer línea, nos indica que el modelo, debe de validar la presencia del campo name esto quiere decir que es un campo requerido.
  • La segunda línea es un caso similar, pero con el campo phone
  • Finalmente, la tercer línea válida que la edad (campo age) sea un número, pero no es requerido, especificado mediante: allow_blank: true

Si en este punto ejecutamos el comando:

rake

Tendremos el resultado de 6 pruebas en verde, es decir 6 pruebas que han sido completadas satisfactoriamente, lo que garantiza que nuestro modelo esté bien validado, según los requerimientos dados.

Conclusiones.

Validar y probar modelos es muy sencillo gracias a los métodos ya definidos por la clase ActiveRecord de Rails. Para probarlos existen algunos frameworks de prueba, tales como RSpec, Minitest, TestUnit. En este ejemplo se utilizó RSpec pues hace que el escribir pruebas sea en un lenguaje muy natural y transparente para el usuario.

Si quieren descargarse el código fuente de este ejemplo, está disponible en el siguiente repositiorio de github:

https://github.com/efigarolam/phonebookexample.git

¡Saludos! Y hasta la próxima.

Eduardo Figarola.

Eduardo Figarola es Ingeniero Junior en Crowd Interactive.