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.