Aprendendo Ruby on Rails – Aula 5
  – Testes com RSpec e Cucumber
                     Maurício Linhares
O que é o RSpec?
   É um ferramenta de Behaviour Driven Development;

   Uma solução simples para implementar testes de
    unidade na sua aplicação;

   Tem suporte direto a Rails para testar modelos,
    helpers e controllers;
Adicionando o RSpec na sua aplicação Rails
– config/environments/test.rb
sudo gem install rspec rspec-rails factory_girl
  remarkable_rails



# configurando as gems necessárias
config.gem 'rspec', :lib => false
config.gem 'rspec-rails', :lib => false
config.gem 'factory_girl'
config.gem 'remarkable_rails', :lib => false
config.gem 'remarkable_activerecord', :lib => false
Gerando o código inicial do RSpec
script/generate rspec
config.gem “factory_girl” ?
   Gem de suporte a testes, ajuda a criar objetos para
    os testes da aplicação;

   É uma “fábrica de objetos” genéricos para evitar a
    repetição de código para testes na aplicação;

   Existem outras gems que executam o mesmo
    trabalho, como “machinist”, todas fazem a mesma
    coisa mas de formas diferentes;

   As fábricas ficam em “spec/factories”;
Criando as sequências –
spec/factories/sequences.rb
Factory.sequence :nome do |n|
 "Nome #{n}"
end

Factory.sequence :email do |n|
 "eu_#{n}@mail.com"
end
Criando as fábricas – usuario_factory.rb
Factory.define :usuario do |f|
 f.nome { Factory.next(:nome) }
 f.email { Factory.next(:email) }
 f.senha "123456"
 f.senha_confirmation "123456"
 f.ultimo_acesso_em { Time.now }
end
Criando as fábricas – produto_factory.rb
Factory.define :produto do |f|
f.nome     { Factory.next(:nome) }
f.preco 10.0
f.descricao "Descrição de exemplo"
end
Criando as fábricas – item_factory.rb

Factory.define :item do |f|
 f.quantidade 5
 f.association :produto, :factory => :produto
 f.association :pedido, :factory => :pedido
end
Criando as fábricas – pedido_factory.rb

Factory.define :pedido do |f|
 f.estado 'carrinho'
end
Criando o spec de produto com Remarkable
require( File.expand_path( File.join( File.dirname(__FILE__), '..',
  'spec_helper' ) ) )

describe Produto do

 context 'ao ser validado' do

  it { should validate_presence_of :nome }
  it { should validate_presence_of :preco }
  it { should validate_numericality_of :preco, :allow_blank => true,
  :greater_than => 0 }

 end

end
Métodos do RSpec
   “describe” – define a classe/objeto que está sendo
    testado;

   “context” – define o escopo no qual o objeto está
    sendo testado

   “it” – define o teste que vai ser executado

   “before” – define um bloco de código que é utilizado
    para inicializar os objetos antes do teste
Criando o spec pra Item
require( File.expand_path( File.join( File.dirname(__FILE__), '..', 'spec_helper' ) ) )

describe Item do

 context 'ao ser associado a outros objetos' do
  it { should belong_to(:pedido) }
  it { should belong_to(:produto) }
 end

 context 'ao se calcular preços' do

  before do
   @produto = Factory(:produto, :preco => 20)
   @item = Factory( :item, :produto => @produto , :quantidade => 6 )
  end

  it 'deve ter como preço 120' { @item.preco_total.should == 120 }

 end

end
Criando o spec para Pedido
context 'em operacoes' do

  before do
   @produto_1 = Factory( :produto, :preco => 10 )
   @produto_2 = Factory( :produto, :preco => 20 )
   @produto_3 = Factory( :produto, :preco => 30 )
   @pedido = Factory( :pedido )
  end

  it 'deve ter como preço total a suma dos preços individuais dos itens' do
    @pedido.adicionar_produto( @produto_1, 2 )
    @pedido.adicionar_produto( @produto_2, 5 )
    @pedido.preco_total.should == 120
  end

end
Criando o spec para o Pedido
context 'em operacoes' do

  context 'na implementação de blank' do

   it 'deve retornar verdadeiro se o pedido estiver vazio' do
     @pedido.should be_blank
   end

   it 'deve retornar false se o pedido não estiver vazio' do
     @pedido.adicionar_produto( @produto_1, 3 )
     @pedido.should_not be_blank
   end

  end

end
Criando o spec de Pedido
context 'ao unir pedidos' do

   before do
    @pedido.adicionar_produto( @produto_1, 2 )
    @pedido.adicionar_produto( @produto_2, 3 )

    @pedido_2 = Factory(:pedido)
    @pedido_2.adicionar_produto( @produto_2, 2 )
    @pedido_2.adicionar_produto( @produto_3, 3 )

    @pedido.unir( @pedido_2 )
   end

   it 'o pedido final deve ter o total de itens dos dois pedidos' do
     @pedido.item_por_produto( @produto_1 ).quantidade.should == 2
     @pedido.item_por_produto( @produto_2 ).quantidade.should == 5
     @pedido.item_por_produto( @produto_3 ).quantidade.should == 3
   end

 end
Criando o spec de Pedido
context 'ao adicionar um produto ao pedido' do

   before do
    @pedido.adicionar_produto( @produto_1, 5 )
   end

   it 'deve adicionar um produto como item do pedido' do
     @pedido.itens.size.should == 1
   end

   it 'deve adicionar o produto corretamente' do
     @pedido.itens.first.produto == @produto_1
   end

   it 'deve aumentar a quantidade de itens ao adicionar o mesmo item denovo' do
     @pedido.adicionar_produto( @produto_1, 3 )
     @pedido.itens.first.quantidade.should == 8
   end

 end
Criando o spec de Pedido
context 'deve remover itens zerados após salvar o pedido' do

   before do
    @pedido.adicionar_produto(@produto_1, 1)
    @pedido.adicionar_produto(@produto_2, 2)
    @pedido.save!
   end

   it 'deve remover o produto' do
     @pedido.itens.last.update_attribute( :quantidade, 0 )
     @pedido.save!
     @pedido.item_por_produto( @produto_2 ).should be_nil
   end

 end
Criando o spec de Usuario
context 'em associações' do

 it { should have_many(:pedidos) }
 it { should have_one(:pedido_atual) }

end

context 'em validações' do

 it { should validate_presence_of( :nome ) }
 it { should validate_presence_of( :email ) }
 it { should validate_acceptance_of(:termos_e_condicoes) }
 it { should validate_presence_of(:senha_em_hash, :if => :senha_necessaria?) }
 it { should validate_length_of(:senha, :within => 4..40, :if => :senha_necessaria?) }
 it { should validate_confirmation_of(:senha, :if => :senha_necessaria?) }

end
Criando a spec de Usuario
context 'em chamadas de senha_necessaria?' do

 before do
  @usuario = Factory(:usuario)
  @usuario.senha = nil
 end

 it 'deve retornar false quando o hash da senha não for vazio' do
   @usuario.senha_necessaria?.should be_false
 end

 it 'deve retornar true quando a senha for preenchida' do
   @usuario.senha = "123456"
   @usuario.senha_necessaria?.should be_true
 end

 it 'deve retornar true quando o hash da senha for vazio' do
   @usuario.senha_em_hash = nil
   @usuario.senha_necessaria?.should be_true
 end

end
Criando a spec de Usuario
context 'ao criar um usuário' do

 before do
  @usuario = Factory.build(:usuario)
 end

  it 'deve enviar um email' do
    UsuariosMailer.should_receive( :deliver_registro
 ).with(@usuario)
    @usuario.save!
  end

end
Criando a spec de Usuario
context 'em chamadas de autenticação' do

 before do
  @usuario = Factory(:usuario,
   :senha => '123456',
   :senha_confirmation => '123456')
 end

 it 'deve autenticar o usuário se estiver com a senha correta' do
   Usuario.autenticar(@usuario.email, '123456').should == @usuario
 end

 it 'não deve autenticar se a senha estiver errada' do
   Usuario.autenticar( @usuario.email, "654321" ).should be_nil
 end

end
Criando spec para SessoesController
context 'em chamadas pra se fazer o login do usuario' do

  before do
   @usuario = Factory(:usuario, :senha => "123456",
 :senha_confirmation => '123456')
  end

 def do_post( senha, email = @usuario.email )
  post :create, :email => email, :senha => senha
 end

end
Criando spec para SessoesController
context 'com dados corretos' do

   it 'deve colocar o usuario na sessao' do
     do_post( '123456' )
     session[:usuario_id].should == @usuario.id
   end

   it 'deve redirecionar para a listagem de produtos' do
     do_post( '123456' )
     response.should redirect_to( produtos_path )
   end

   it 'deve colocar uma mensagem na flash' do
     do_post('123456')
     flash[:aviso].should == "Seja bem vindo a nossa loja, #{@usuario.nome}"
   end

 end
Criando spec para SessoesController
context 'com dados incorretos' do

   it 'deve mostrar a página new' do
     do_post( 'falso!' )
     response.should render_template(:new)
   end

  it 'deve colocar uma mensagem na flash' do
    do_post('falso!')
    response.flash[:erro].should == "Não foi encontrado um
 usuário com o email e a senha que você forneceu"
  end

 end

Cuso Ruby - Aula 05 - Testes com RSpec

  • 1.
    Aprendendo Ruby onRails – Aula 5 – Testes com RSpec e Cucumber Maurício Linhares
  • 2.
    O que éo RSpec?  É um ferramenta de Behaviour Driven Development;  Uma solução simples para implementar testes de unidade na sua aplicação;  Tem suporte direto a Rails para testar modelos, helpers e controllers;
  • 3.
    Adicionando o RSpecna sua aplicação Rails – config/environments/test.rb sudo gem install rspec rspec-rails factory_girl remarkable_rails # configurando as gems necessárias config.gem 'rspec', :lib => false config.gem 'rspec-rails', :lib => false config.gem 'factory_girl' config.gem 'remarkable_rails', :lib => false config.gem 'remarkable_activerecord', :lib => false
  • 4.
    Gerando o códigoinicial do RSpec script/generate rspec
  • 5.
    config.gem “factory_girl” ?  Gem de suporte a testes, ajuda a criar objetos para os testes da aplicação;  É uma “fábrica de objetos” genéricos para evitar a repetição de código para testes na aplicação;  Existem outras gems que executam o mesmo trabalho, como “machinist”, todas fazem a mesma coisa mas de formas diferentes;  As fábricas ficam em “spec/factories”;
  • 6.
    Criando as sequências– spec/factories/sequences.rb Factory.sequence :nome do |n| "Nome #{n}" end Factory.sequence :email do |n| "eu_#{n}@mail.com" end
  • 7.
    Criando as fábricas– usuario_factory.rb Factory.define :usuario do |f| f.nome { Factory.next(:nome) } f.email { Factory.next(:email) } f.senha "123456" f.senha_confirmation "123456" f.ultimo_acesso_em { Time.now } end
  • 8.
    Criando as fábricas– produto_factory.rb Factory.define :produto do |f| f.nome { Factory.next(:nome) } f.preco 10.0 f.descricao "Descrição de exemplo" end
  • 9.
    Criando as fábricas– item_factory.rb Factory.define :item do |f| f.quantidade 5 f.association :produto, :factory => :produto f.association :pedido, :factory => :pedido end
  • 10.
    Criando as fábricas– pedido_factory.rb Factory.define :pedido do |f| f.estado 'carrinho' end
  • 11.
    Criando o specde produto com Remarkable require( File.expand_path( File.join( File.dirname(__FILE__), '..', 'spec_helper' ) ) ) describe Produto do context 'ao ser validado' do it { should validate_presence_of :nome } it { should validate_presence_of :preco } it { should validate_numericality_of :preco, :allow_blank => true, :greater_than => 0 } end end
  • 12.
    Métodos do RSpec  “describe” – define a classe/objeto que está sendo testado;  “context” – define o escopo no qual o objeto está sendo testado  “it” – define o teste que vai ser executado  “before” – define um bloco de código que é utilizado para inicializar os objetos antes do teste
  • 13.
    Criando o specpra Item require( File.expand_path( File.join( File.dirname(__FILE__), '..', 'spec_helper' ) ) ) describe Item do context 'ao ser associado a outros objetos' do it { should belong_to(:pedido) } it { should belong_to(:produto) } end context 'ao se calcular preços' do before do @produto = Factory(:produto, :preco => 20) @item = Factory( :item, :produto => @produto , :quantidade => 6 ) end it 'deve ter como preço 120' { @item.preco_total.should == 120 } end end
  • 14.
    Criando o specpara Pedido context 'em operacoes' do before do @produto_1 = Factory( :produto, :preco => 10 ) @produto_2 = Factory( :produto, :preco => 20 ) @produto_3 = Factory( :produto, :preco => 30 ) @pedido = Factory( :pedido ) end it 'deve ter como preço total a suma dos preços individuais dos itens' do @pedido.adicionar_produto( @produto_1, 2 ) @pedido.adicionar_produto( @produto_2, 5 ) @pedido.preco_total.should == 120 end end
  • 15.
    Criando o specpara o Pedido context 'em operacoes' do context 'na implementação de blank' do it 'deve retornar verdadeiro se o pedido estiver vazio' do @pedido.should be_blank end it 'deve retornar false se o pedido não estiver vazio' do @pedido.adicionar_produto( @produto_1, 3 ) @pedido.should_not be_blank end end end
  • 16.
    Criando o specde Pedido context 'ao unir pedidos' do before do @pedido.adicionar_produto( @produto_1, 2 ) @pedido.adicionar_produto( @produto_2, 3 ) @pedido_2 = Factory(:pedido) @pedido_2.adicionar_produto( @produto_2, 2 ) @pedido_2.adicionar_produto( @produto_3, 3 ) @pedido.unir( @pedido_2 ) end it 'o pedido final deve ter o total de itens dos dois pedidos' do @pedido.item_por_produto( @produto_1 ).quantidade.should == 2 @pedido.item_por_produto( @produto_2 ).quantidade.should == 5 @pedido.item_por_produto( @produto_3 ).quantidade.should == 3 end end
  • 17.
    Criando o specde Pedido context 'ao adicionar um produto ao pedido' do before do @pedido.adicionar_produto( @produto_1, 5 ) end it 'deve adicionar um produto como item do pedido' do @pedido.itens.size.should == 1 end it 'deve adicionar o produto corretamente' do @pedido.itens.first.produto == @produto_1 end it 'deve aumentar a quantidade de itens ao adicionar o mesmo item denovo' do @pedido.adicionar_produto( @produto_1, 3 ) @pedido.itens.first.quantidade.should == 8 end end
  • 18.
    Criando o specde Pedido context 'deve remover itens zerados após salvar o pedido' do before do @pedido.adicionar_produto(@produto_1, 1) @pedido.adicionar_produto(@produto_2, 2) @pedido.save! end it 'deve remover o produto' do @pedido.itens.last.update_attribute( :quantidade, 0 ) @pedido.save! @pedido.item_por_produto( @produto_2 ).should be_nil end end
  • 19.
    Criando o specde Usuario context 'em associações' do it { should have_many(:pedidos) } it { should have_one(:pedido_atual) } end context 'em validações' do it { should validate_presence_of( :nome ) } it { should validate_presence_of( :email ) } it { should validate_acceptance_of(:termos_e_condicoes) } it { should validate_presence_of(:senha_em_hash, :if => :senha_necessaria?) } it { should validate_length_of(:senha, :within => 4..40, :if => :senha_necessaria?) } it { should validate_confirmation_of(:senha, :if => :senha_necessaria?) } end
  • 20.
    Criando a specde Usuario context 'em chamadas de senha_necessaria?' do before do @usuario = Factory(:usuario) @usuario.senha = nil end it 'deve retornar false quando o hash da senha não for vazio' do @usuario.senha_necessaria?.should be_false end it 'deve retornar true quando a senha for preenchida' do @usuario.senha = "123456" @usuario.senha_necessaria?.should be_true end it 'deve retornar true quando o hash da senha for vazio' do @usuario.senha_em_hash = nil @usuario.senha_necessaria?.should be_true end end
  • 21.
    Criando a specde Usuario context 'ao criar um usuário' do before do @usuario = Factory.build(:usuario) end it 'deve enviar um email' do UsuariosMailer.should_receive( :deliver_registro ).with(@usuario) @usuario.save! end end
  • 22.
    Criando a specde Usuario context 'em chamadas de autenticação' do before do @usuario = Factory(:usuario, :senha => '123456', :senha_confirmation => '123456') end it 'deve autenticar o usuário se estiver com a senha correta' do Usuario.autenticar(@usuario.email, '123456').should == @usuario end it 'não deve autenticar se a senha estiver errada' do Usuario.autenticar( @usuario.email, "654321" ).should be_nil end end
  • 23.
    Criando spec paraSessoesController context 'em chamadas pra se fazer o login do usuario' do before do @usuario = Factory(:usuario, :senha => "123456", :senha_confirmation => '123456') end def do_post( senha, email = @usuario.email ) post :create, :email => email, :senha => senha end end
  • 24.
    Criando spec paraSessoesController context 'com dados corretos' do it 'deve colocar o usuario na sessao' do do_post( '123456' ) session[:usuario_id].should == @usuario.id end it 'deve redirecionar para a listagem de produtos' do do_post( '123456' ) response.should redirect_to( produtos_path ) end it 'deve colocar uma mensagem na flash' do do_post('123456') flash[:aviso].should == "Seja bem vindo a nossa loja, #{@usuario.nome}" end end
  • 25.
    Criando spec paraSessoesController context 'com dados incorretos' do it 'deve mostrar a página new' do do_post( 'falso!' ) response.should render_template(:new) end it 'deve colocar uma mensagem na flash' do do_post('falso!') response.flash[:erro].should == "Não foi encontrado um usuário com o email e a senha que você forneceu" end end