Vicente Rodríguez

Nov. 1, 2016

Tutorial de rspec con ruby on rails

En este tutorial usaremos la gema rspec para hacer tests de controladores y modelos en ruby on rails.

En este link esta el código del tutorial.

Agregando las gemas

Necesitaremos agregar las gemas siguientes al gemfile:


gem 'valid_email'



group :development, :test do

  gem 'shoulda-matchers'

  gem 'rspec-rails'

  gem 'factory_girl_rails', "~>4.0"

  gem 'capybara'

end

Explicare la utilidad de cada gema después, la que importa en este momento es rspec que nos permitirá tener un entorno para probar nuestro código, para instalarla ponemos en la consola:


rails generate rspec:install

Esto nos creara una carpeta llamada spec donde se guardaran nuestros test y la configuración.

Creando controladores y modelos

Crearemos un modelo llamado post con los siguientes atributos:


rails g model Post title description:text 

y otro modelo llamado comment:


rails g Comment email body:text post:references

Ahora crearemos los controladores de los modelos:


rails g controller posts index show new edit create



rails g controller comments new create

dentro de posts_controller necesitaremos tener el siguiente código:


class PostsController < ApplicationController

  def index

    @posts = Post.all

  end



  def show

    @post = Post.find(params[:id])

  end



  def edit

  end



  def new

    @post = Post.new

  end



  def create

    @post = Post.new(post_params)

    if @post.save

      render json: { success: "all okay" }

    else

      render :new

    end

  end



  private

    def post_params

      params.require(:post).permit(:title, :description)

    end



end

y en comments_controller:


class CommentsController < ApplicationController



  def new

  end



  def create

    @post = Post.find_by(params[:post_id])

    @comment = @post.comments.new(comment_params)

    if @comment.save

      render json: { success: "all okay" }

    else

      render :new

    end

  end



  private

    def comment_params

      params.require(:comment).permit(:email, :body)

    end

end

Como se puede notar en ambos controladores nos centraremos en hacer tests sobre la función create, también se pueden crear test para las funciones edit, show, index pero para hacer corto el tutorial solo nos centraremos en create e index.

Creando los tests

Como podemos notar a la hora de crear nuevos controladores o modelos automáticamente rspec crea carpetas dentro de spec que contienen el código base para empezar a codear las pruebas.

Factories

Antes de iniciar con las pruebas necesitamos crear modelos fake para las pruebas, de esto se encarga la gema de factory-girl que instalamos previamente, como podemos notar tenemos una carpeta que se llama factories y dentro los dos modelos, cada modelo tiene que tener el siguiente código:

Posts


FactoryGirl.define do

  factory :post do

    title "MyString"

    description "Tengo un texto largo para que la validacion pase"

  end

end

Comments


FactoryGirl.define do

  factory :comment do

    email "vincent@hotmail.com"

    body "muy buen post!"

    association :post, factory: :post

  end

end

Crear una factory es igual que crear un nuevo registro, incluso podemos tener asociaciones con otros modelos.

Tests models

Empezaremos a crear las pruebas para los modelos, estas pruebas verifican que los modelos tengan las validaciones necesarias, usaremos la gema shoulda-matchers para comprobar los modelos:

Post spec


require 'rails_helper'



RSpec.describe Post, type: :model do

  it { should validate_presence_of(:title) }

  it { should validate_presence_of(:description) }

  it { should validate_length_of(:description).is_at_least(20) }

end



Comment spec


require 'rails_helper'



RSpec.describe Comment, type: :model do

  it { should validate_presence_of(:email) }

  it { should_not allow_value("vincent@hotmail").for(:email) }

  it { should allow_value("vincent@hotmail.com").for(:email) }



  it { should validate_presence_of(:body) }

  it { should belong_to(:post) }

end

El codigo es muy sencillo de entender, simplemente ponemos a prueba las validaciones para nuestros modelos, si corremos el comando:


rspec spec/models

No es necesario indicar la ruta para correr las pruebas pero en este caso solo queremos ejecutar las pruebas de los modelos entonces indicamos la carpeta

Las pruebas no pasan y esto se debe a que no tenemos validaciones en nuestros modelos, pasamos a agregarlas:

Comment


class Comment < ActiveRecord::Base

  belongs_to :post

  validates :email, presence: true, email: true

  validates :body, presence: true

end



Post


class Post < ActiveRecord::Base

  validates :title, presence: true

  validates :description, presence: true, length: { minimum: 20 }

  has_many :comments

end

Volvemos a correr las pruebas:


rspec spec/models

Las 8 pruebas pasan, como podemos ver la palabra clave validates crea un requisito para un atributo del modelo, ya sea que es necesario que contenga un titulo o que este tenga mas de 10 caracteres, etc.

Tests controllers

Pasemos a crear tests para los controllers, estos son diferentes a los de los modelos

Comments controller spec


require 'rails_helper'



RSpec.describe PostsController, type: :controller do



  describe "GET #index" do

    it "returns http success" do

      get :index

      expect(response).to have_http_status(:success)

    end

  end



  describe "POST #create" do

    let(:post_one) {

      { title: "Hola mundo", description: "descripcion larga para que la validacion pase"}

    }



    before :each do

      post :create, { post: post_one }

    end



    it { expect(response).to have_http_status 200 }



    it "add post" do

      expect{

        post :create, { post: post_one }

      }.to change(Post,:count).by(1)

    end

  end



end



En las pruebas de los controladores empezamos por indicar que ruta o acción queremos probar, esto se hace dentro de un bloque llamado describe, dentro de este bloque tenemos el código correspondiente a las pruebas, cada prueba se declara en otro bloque llamado it al que le podemos pasar la descripción de lo que la prueba hace.

La primera acción que probamos es index, esta es sencilla y solo comprobaremos que cuando vayamos a esa ruta nos devuelva un código http 200 indicando que todo esta bien, el código de la prueba funciona de la siguiente manera, primero obtenemos la ruta:


get :index

Esta nos regresa dos variables request y response que contienen diferente información, en este caso usaremos response para acceder al código http que la pagina nos devuelve, en la segunda linea usamos expect para comprobar que el valor de la izquierda sea igual al de la derecha en este caso que el response nos devuelva un código 200 o success. También podemos probar si la pagina responde con un html o json, si hay datos en la pagina index, etc.

El siguiente test se encarga de comprobar la ruta de create:

En este caso creamos una variable con un objeto que contiene los atributos que necesita un objeto Post, como en este caso tenemos dos pruebas y las dos repiten código usamos un bloque before :each, este bloque se ejecuta por cada prueba que exista dentro del bloque describe, en este caso queremos hacer un post a la ruta create y pasar el objeto Post que acabamos de crear, la primera prueba comprueba que la pagina nos regrese un código 200 de que todo salió bien y la segunda comprueba que el post se haya creado y guardado.

En este caso nos devuelve un código 200 porque no tenemos redirección a otra ruta pero generalmente al crear un nuevo registro queremos que la pagina nos mande al nuevo registro o a la pagina principal, en ese caso lo que comprobaríamos es que nos devolviera un código 301.

Ahora vamos a realizar pruebas del controlador de comments:


require 'rails_helper'



RSpec.describe CommentsController, type: :request do



  describe "POST #create" do

   p = FactoryGirl.create(:post)

   let(:comment) {

    { email: "vincent@hotmail.com", body: "muy buen post", post: p }

   }

   before :each do

      post post_comments_path(p.id), { comment: comment }

   end



   it { expect(response).to have_http_status 200 }

  end

end

Aquí vemos en acción a las factories que creamos previamente, en realidad solo a la factory de post ya que la de comment no la necesitaremos, como todos los comentarios al ser creados necesitan un post al cual pertenecen le pedimos a factory girl que nos cree un nuevo post con los datos que ya están previamente indicados, también podríamos crear un nuevo modelo desde el código de la prueba pero lo mejor es tener organizado y dejar los datos del modelo en el archivo de la factory, lo que si necesitamos crear desde cero son los datos del comentario y pasar el post como atributo, dentro del bloque before each hacemos la petición de tipo post a la ruta en donde se crean los comentarios post_comments_path(p.id) esta ruta parece un poco rara pero la razón detrás de esto es que los comentarios no tienen rutas en solitario porque siempre pertenecen a un post y los vemos desde la ruta de los posts, el archivo routes.rb tendría que estar de la siguiente manera:


resources :posts do 

  resources :comments

end

También la ruta necesita saber a que post le queremos agregar o crear el comentario por eso le pasamos el id.