domingo, 17 de outubro de 2010

Rails 3, RSpec 2, Devise a MongoDB

1

Estive estudando o Rails 3 (Atualmente 3.0.1 para o rails), e também estudando um pouco de NoSQL, com o MongoDB, ainda pretendo me dedicar um pouco para aprender sobre o Redis, mas por enquanto estou vendo o Rails. Gosto de usar para autenticação o Devise, um projeto brasileiro, que se tornou bem famoso pelo mundo Rails. Para ORM gostei do Mongoid, e ainda mais pelo devise ter suporte ao mongoid ao invez do ActiveRecord.

Iniciando

Sobre o mongoid não há muito o que falar, a documentação presente no site é completa, e funciona muito bem, vale lembrar que arquitetura de um banco relacional é bem diferente de um NoSQL, e algumas definições já padrão na nossa cabeça devem ser desfeitas.

O RSpec-Rails vem configurado para rodar sobre o ActiveRecord e quando vc executar um rails g rspec:install ira gerar toda a estrutura padrão do rspec, incluindo a pasta spec que contem os testes. Primeiramente vamos editar o arquivo spec/spec_helper.rb

  1. RSpec.configure do |config|  
  2.   require 'database_cleaner'  
  3.     
  4.   config.before(:suitedo  
  5.     DatabaseCleaner.strategy = :truncation  
  6.     DatabaseCleaner.orm = "mongoid"  
  7.   end  
  8.   
  9.   config.before(:eachdo  
  10.     DatabaseCleaner.clean  
  11.   end  
  12.   config.mock_with :rspec  
  13.   config.include Devise::TestHelpers, :type => :controller # Essa linha adiciona os helpers do Devise para o RSpec.  
  14. end  

A adição da gem DatabaseCleaner (que deve estar declarada no Gemfile), serve para limpar os resultados dos testes apos a execução, como podem reparar o uso de fixtures é desabilidado para o Mongo, somente para o ActiveRecord, sem o uso de fixtures os testes são mantidos na base, o que não é interessante.

Testando os controllers

Criei um modelo simples com o devise, apesar da documentação do devise indicar a criação de um modelo Admin para esses casos, preferia adotar o uso de um flag para poder indicar se o usuário é um administrador, acho isso um tanto mais simples.
Adicionei também no application_controller.rb um metodo filtro que indica se o usuário é um adminsitrador

  1. protected  
  2.   def admin_user!      
  3.     if authenticate_user!        
  4.       unless current_user.admin          
  5.         flash[:warning] = "Acesso Negado"  
  6.         redirect_to root_url  
  7.       end  
  8.     end  
  9.   end  
reparem que usei o filtro authenticated_user! que é do devise pra indicar que o usuário está logado no sistema.
meu controller admin_controller.rb
  1. class AdminController < ApplicationController  
  2.   before_filter :admin_user!  
  3.     
  4.   def index  
  5.   end  
  6.   
  7. end  
é um controller bem simples, mas para garantirmos a segurança do aplicativo, e se algum dia nosso filtro precisar ser ajustado precisamos garantir que o acesso a esse controller seja restrito sempre, e quem alterar deve ter noção da quebra do teste. Em alguns dos meus testes, normalmente em fixtures, eu pego um hash que representa o codigo BCrypt da senha (padrão do Devise), mas como aqui no mongo não tem fixutes, parti pra outro ponto.
  1. require 'spec_helper'  
  2.   
  3. describe AdminController do  
  4.       
  5.   describe "GET 'index'" do      
  6.     def do_action_with_user(admin)  
  7.       @user = User.create!(:name => "Test":email => "test@test.com",   
  8.         :password => "testet",  
  9.         :password_confirmation => "testet",  
  10.         :admin => admin)          
  11.       sign_in @user  
  12.       get 'index'  
  13.     end  
  14.     #1  
  15.     it "should not be successful when no user was logged in" do  
  16.       get 'index'  
  17.       flash[:alert].should_not be_nil  
  18.       response.should_not be_success  
  19.     end  
  20.     #2  
  21.     it "should not be successful when no admin user was logged in" do  
  22.       do_action_with_user(false)        
  23.       flash[:warning].should_not be_nil        
  24.       response.should_not be_success  
  25.     end  
  26.     #3  
  27.     it "should be successful when admin user was logged in" do  
  28.       do_action_with_user(true)  
  29.       response.should be_success  
  30.     end  
  31.   end  
  32.   
  33. end  
bem simples, um metodo dry pra fazer o registro do usuario e usar o metodo auxiliar do devise para o login (sign_in):
  • o teste 1 é um teste para acesso sem login, no caso uma chamada a action index do controller admin sem um usuário logado.
  • teste 2 teste para um usuário que não é administrador, repare que testo se o flash esta preenchido
  • teste 3 testa a ação de sucesso, no caso o acesso de um usuário administrador
Inicialmente esse teste se fez suficiente para dizer que agora a classe está segura.

Outras notas

Tive alguns problemas no começo do uso do RSpec 2 em views, e vou listar aqui alguns deles:

  1. assign :name"Teste"  
ao invez de
  1. assigns[:name] = "Teste"  
Outra mudança é que em views você não deve mais usar o response, mas usar o rendered. Também deixou de existir o metodo have_tag, para verificar o conteudo de uma tag, veja abaixo:
  1. #RSpec 3  
  2. assert_select('p'"Teste")  
  3. #RSpec 2  
  4. response.have_tag 'p'"Teste"  


Espero que isso seja util pra mais alguem, pelo menos eu não vou mais esquecer :D

1 comentários:

Unknown disse...

Muito útil. Ajudou bastante. Valew!

 
Design by ThemeShift | Bloggerized by Lasantha - Free Blogger Templates | Best Web Hosting