2011-12-10 39 views
0

我是Rails开发的新手 - 我读了很多书,现在正在头一次潜水。我有一个用User模型设置的基本应用程序,以及通过Authlogic设置的UserSession。我正在使用RSpec进行测试。Rails 3.1,RSpec和Authlogic的问题

我已经把所有的测试通过了昨天早些时候(类型,见下文),然后实现了密码重置功能(通过电子邮件,HTTP),现在我的测试很好的一部分已经打破 - 似乎是登录/ Authlogic。

我上面提到的是,自从我开始使用Authlogic以来,我一直在测试中遇到问题......它似乎并不想登录。在Request specs(也许这是最好在单独的问题中解决)除非我在该规范中手动注册用户(signup_path,fill_in等),否则我无法登录(通过访问login_path和fill_in字段)。工厂似乎没有伎俩......我看过的每个地方都说要做@user = Factory.create(:user),然后是UserSession.create(@user),但它往往会失败。有时我可以使用spec_helper方法(sign_in(@user))来登录,有时候它会失败。

我应该注意到,尽管我的测试都说了什么,但一切都在开发模式下工作。关于TDD的阅读首先让我对测试产生了兴趣,但这削弱了我的动力!

反正......这里有一些相关的代码。请让我知道还有什么是必需的。

典型错误:

Failure/Error: response.should render_template('users/show')expecting <"users/show"> but rendering with <"users/new,>..... 

6) UsersController GET index for non admin users should protect the page 
    Failure/Error: response.should redirect_to(root_path) 
     Expected response to be a redirect to <http://test.host/> but was a redirect to <http://test.host/user_sessions/new> 
    # ./spec/controllers/users_controller_spec.rb:72:in `block (4 levels) in <top (required)>' 

    11) UsersController POST 'create' success should redirect to the user show page 
    Failure/Error: response.should redirect_to(user_path(assigns(:user))) 
     Expected response to be a redirect to <http://test.host/users/5> but was a redirect to <http://test.host/> 
    # ./spec/controllers/users_controller_spec.rb:160:in `block (4 levels) in <top (required)>' 

    13) UsersController POST 'create' success should be logged in 
Failure/Error: UserSession.find.should_not be_nil 
    expected: not nil 
     got: nil 

应用控制器

class ApplicationController < ActionController::Base 
    protect_from_forgery 
    helper_method :logged_in?, :current_user, :admin? 
    force_ssl if Rails.env.production? 

    private 

    def logged_in? # allows use of be_logged_in in RSpec 
    !current_user.nil? 
    end 

    def admin? # For RSpec 
    current_user.admin? 
    end 

    def current_user_session 
    return @current_user_session if defined?(@current_user_session) 
    @current_user_session = UserSession.find 
    end 

    def current_user 
    return @current_user if defined?(@current_user) 
    @current_user = current_user_session && current_user_session.record 
    end 

    def require_user 
    unless current_user 
     store_location 
     flash[:error] = "You must be logged in to access this page" 
     redirect_to new_user_session_path 
     return false 
    end 
    end 

    def require_no_user 
    if current_user 
     store_location 
     flash[:error] = "You must be logged out to access this page" 
     redirect_to user_path(current_user.id) 
     return false 
    end 
    end 

    def require_admin 
    unless (current_user && current_user.admin?) 
     redirect_to root_path 
    end 
    end 

    def store_location 
    session[:return_to] = request.fullpath 
    end 

    def redirect_back_or_default(default) 
    redirect_to(session[:return_to] || default) 
    session[:return_to] = nil 
    end 

end 

UserSessions控制器

class UserSessionsController < ApplicationController 
    before_filter :require_no_user, :only => [:new, :create] 
    before_filter :require_user, :only => :destroy 

    def new 
    @user_session = UserSession.new 
    @title = "Log In" 
    end 

    def create 
    @user_session = UserSession.new(params[:user_session]) 
    if @user_session.save 
     user = @user_session.user 
     flash[:success] = "Login successful!" 
     redirect_back_or_default user_path(user.id) 
    else 
     flash[:error] = "Incorrect user name or password" 
     redirect_to new_user_session_path 
    end 
    end 

    def destroy 
    current_user_session.destroy 
    flash[:notice] = "You have been logged out" 
    redirect_back_or_default(new_user_session_path) 
    end 

end 

用户控制器:

class UsersController < ApplicationController 
    before_filter :require_no_user, :only => [:new, :create] 
    before_filter :require_user, :only => [:index, :show, :edit, :update, :destroy, :change_password] 
    before_filter :require_admin, :only => [:index, :destroy] 
    before_filter :check_for_last, :only => [:destroy] 

    def new 
    @title = "Sign Up" 
    @user = User.new 
    end 

    def index 
    @title = "User Index" 
    @users = User.find(:all) 
    end 

    def create 
    @user = User.new(params[:user]) 
    if @user.save 
     flash[:notice] = "Thanks for signing up, we've delivered an email to you with instructions on how to complete your registration!" 
     @user.deliver_verification_instructions! 
     redirect_to root_url 
     #UserSession.create @user # Turned off Authlogic automatic session maintenance. See https://github.com/binarylogic/authlogic/issues/262 
    else 
     @title = "Sign Up" 
     render :new 
    end 
    end 

    def show 
    if current_user.admin? 
     @user = User.find(params[:id]) || current_user 
    else 
     @user = current_user 
    end 
    @title = "Profile for #{@user.name}" 
    end 

    def edit 
    if current_user.admin? && (params[:id] != current_user.id) 
     redirect_to user_path(current_user.id), :notice => "Although you are an admin, you tried to edit someone else's profile. Do this otherwise (console)  to avoid errors" 
    end 
    @user = current_user 
    @title = "Edit User: #{@user.name}" 
    end 

    def update 
    @user = @current_user # makes our views "cleaner" and more consistent 
    if @user.update_attributes(params[:user]) 
     flash[:success] = "Edited your profile!" 
     redirect_to user_path(@user) 
    else 
     render :edit 
    end 
    end 

    def destroy 
    User.find(params[:id]).destroy 
    flash[:success] = "User destroyed." 
    redirect_to users_path 
    end 

    def change_password 
    @user = current_user 
    @title = "Reset Password" 
    end 

    def update_password 
    @user = current_user 
    redirect_to root_path unless @user 
    @user.password = params[:user][:password] 
    @user.password_confirmation = params[:user][:password_confirmation] 
    if @user.save 
     UserSession.create @user # Turned off Authlogic automatic session maintenance. See https://github.com/binarylogic/authlogic/issues/262 
     flash[:success] = "Password successfully updated" 
     redirect_to user_path(@user.id) 
    else 
     render :action => :change_password 
    end 
    end 

    private 

    def check_for_last 
    if (User.all.size == 1) 
     flash[:notice] = "You are trying to delete the last user... do that in console" 
     redirect_to users_path 
     return false 
    end 
    end 

end 

用户模型:

class User < ActiveRecord::Base 

    acts_as_authentic do |c| 
    c.logged_in_timeout(30.minutes) 
    c.login_field = :email 
    c.maintain_sessions = false 
    end 

    attr_accessible :name, :email, :password, :password_confirmation 
    validates :name, :length => (2..50) 
    validates :password, :length => {:minimum => 4} 

    def deliver_verification_instructions! 
    reset_perishable_token! 
    UserMailer.verification_instructions(self).deliver 
    end 

    def verify! 
    self.verified = true 
    self.save 
    end 

    def deliver_password_reset_instructions! 
    reset_perishable_token! 
    UserMailer.password_reset_instructions(self).deliver 
    end 

end 

UserSession型号:

class UserSession < Authlogic::Session::Base  
    logout_on_timeout true 
    consecutive_failed_logins_limit 10 
    failed_login_ban_for 1.hour 

    validate :check_if_verified 

    private 

    def check_if_verified 
    errors.add(:base, "You have not yet verified your account") unless attempted_record && attempted_record.verified 
    end 
end 

UserSession控制器规格

用户控制器规格

require 'spec_helper' 

describe UsersController do 
    render_views 

    before(:each) do 
    activate_authlogic 
    end 

    describe "GET 'new'" do 

    it "should only work if not logged in" do 
     user = Factory.create(:user) 
     sign_in(user) 
     get :new 
     flash[:error].should =~ /must be logged out/i 
     response.should redirect_to user_path(user) 
    end 
    end 

    describe "GET index" do 

    before(:each) do 
     @admin_user = Factory.create(:user, :admin => true) 
     @signed_in_user = Factory.create(:user) 
     @user1 = Factory.create(:user) 
     @user2 = Factory.create(:user) 
     @user3 = Factory.create(:user) 
     @users = [@admin_user, @signed_in_user, @user1, @user2, @user3] 
    end 

    describe "for non signed in users" do 
     it "should deny access" do 
     get :index 
     flash[:error].should =~ /must be logged in/i 
     response.should redirect_to new_user_session_path 
     end  
    end 

    describe "for non admin users" do 
     it "should protect the page" do 
     sign_in(@signed_in_user) 
     get :index 
     response.should redirect_to(root_path) 
     flash[:error].should be_nil 
     flash[:success].should be_nil 
     flash[:notice].should be_nil    
     end 
    end 

    describe "for admin users" do 

     before(:each) do 
     sign_in(@admin_user) 
     end  
    end  
    end 

    describe "POST 'create'" do 

    describe "failure" do 

     before(:each) do 
     @attr = {:name=>"", :email=>"", :password=>"", :password_confirmation=>""} 
     end 

     it "should not create a user" do 
     lambda do 
      post :create, :user => @attr 
     end.should_not change(User, :count) 
     end 

     it "should render the 'new' page" do 
     post :create, :user => @attr 
     response.should render_template('new') 
     response.should have_selector("div.error_messages") 
     end 

     it "should only create a user if not logged in" do 
     @another_user = Factory.create(:user) 
     sign_in(@another_user) 
     @valid_attr = { :name=> 'New User', :email => '[email protected]',:password => 'foobar', :password_confirmation => 'foobar' } 
     post :create, :user => @attr 
     flash[:error].should =~ /must be logged out/i 
     response.should redirect_to user_path(@another_user) 
     end 

    end #failure 

    describe "success" do 

     before(:each) do 
     @attr = { :name=> 'New User', :email => '[email protected]',:password => 'foobar', :password_confirmation => 'foobar' } 
     end 

     it "should create a user" do 
     lambda do 
      post :create, :user => @attr 
     end.should change(User, :count).by(1) 
     end 

     it "should redirect to the user show page" do 
     post :create, :user => @attr 
     assigns[:user].email.should == "[email protected]" 
     response.should redirect_to(user_path(assigns(:user))) 
     end 

     it "should have a welcome message" do 
     post :create, :user => @attr 
     flash[:success].should =~ /signed/i 
     end 

     it "should be logged in" do 
     post :create, :user => @attr 
     UserSession.find.should_not be_nil 
     end 

    end #success 
    end # post create 

    describe "GET edit" do 

    before(:each) do 
     @valid_user = Factory.create(:user) 
     sign_in(@valid_user) 
    end 

    describe "success" do  

     it "should have the right title" do 
     get :edit, :id => @valid_user 
     response.should have_selector("title", :content => "Edit User: #{@valid_user.name}") 
     end 

    end # success 

    describe "failure" do 

     it "should not get edit if logged out" do 
     sign_out 
     get :edit, :id => @valid_user 
     flash[:error].should =~ /must be logged in/i 
     response.should redirect_to new_user_session_path 
     end 

    end # failure 
    end # GET edit 

    describe "GET show" do 

    before(:each) do 
     @valid_user = Factory.create(:user) 
     sign_in(@valid_user) 
    end 

    describe "success" do 

     it "should get show if logged in" do 
     get :show, :id => @valid_user 
     response.should be_success 
     end 

    end # success 

    describe "failure" do 

     it "should not get show if logged out" do 
     sign_out 
     get :show, :id => @valid_user 
     flash[:error].should =~ /must be logged in/i 
     response.should redirect_to new_user_session_path 
     end 

     it "should only be able to see their own page" do 
     @another_user = Factory.create(:user) 
     get :show, :id => @another_user 
     response.should have_selector("title", :content => "Profile for #{@valid_user.name}") 
     end 

    end # failure 

    describe "admin users" do 

     before(:each) do 
     @admin_user = Factory.create(:user, :admin => true) 
     sign_out 
     sign_in(@admin_user) 
     end 

     it "should get their own show page if that is the selection" do 
     get :show, :id => @admin_user 
     response.should have_selector("title", :content => "Profile for #{@admin_user.name}") 
     end 

     it "should also be able to get other people's show page if that is the selection" do 
     get :show, :id => @valid_user 
     response.should have_selector("title", :content => "Profile for #{@valid_user.name}") 
     end 

     it "should not show admin content on admin login" do 
     sign_in(@valid_user) 
     get :show, :id => @valid_user 
     response.should_not have_selector("section.admin>ul>li>a", :content => "Show Users", :href => users_path) 
     end 

    end 

    end # GET show 

    describe "PUT update" do 

    before(:each) do 
     @valid_user = Factory.create(:user) 
     sign_in(@valid_user) 
    end 

    describe "success" do 

     before(:each) do 
     @new_attrs = { :name => "new name", :email => "[email protected]", :password => @valid_user.password, :password_confirmation => @valid_user.password} 
     end 

     it "should correctly update the user with valid attributes" do 
     put :update, :id => @valid_user.id, :user => @new_attrs 
     assigns[:user].name.should == "new name" 
     assigns[:user].email.should == "[email protected]" 
     User.find(@valid_user.id).email.should == "[email protected]" 
     end 

     it "should redirect to the user show page" do 
     put :update, :id => @valid_user.id, :user => @new_attrs 
     response.should redirect_to user_path(@valid_user) 
     flash[:success].should =~ /edited/i 
     end 

    end # success 

    describe "failure" do 

     before(:each) do 
     @bad_attrs = { :name => "", :email => "nottaken"} 
     end 

     it "should not update user attributes if invalid" do 
     original_email = @valid_user.email 
     put :update, :id => @valid_user, :user => @bad_attrs 
     @valid_user.reload 
     @valid_user.email.should == original_email 
     logged_in?.should be_true 
     User.find_by_email(original_email).should_not be_nil 
     end 

     it "should re-render the edit page" do 
     put :update, :id => @valid_user, :user => @bad_attrs 
     #controller.stub!(:require_user).and_return(true) 
     response.should render_template('edit') 
     logged_in?.should be_true 
     end 

     it "should not allow edit, even with valid attributes, if not logged in" do 
     sign_out 
     original_email = @valid_user.email 
     put :update, :id => @valid_user, :user => @new_attrs 
     @valid_user.reload 
     @valid_user.email.should == original_email 
     response.should redirect_to new_user_session_path 
     logged_in?.should be_false 
     end 

     it "should not allow you to take another user's email" do 
     @another_user = Factory.create(:user) 
     put :update, :id => @valid_user, :user => { :name => "valid name", :email => @another_user.email} 
     response.should render_template('edit') 
     response.should have_selector("div.error_messages") 
     end 

    end #failure 

    end # post update 

    describe "DELETE destroy" do 

    before(:each) do 
     @valid_user = Factory.create(:user) 
    end 

    describe "as a non-signed-in user" do 
     it "should deny access" do 
     delete :destroy, :id => @valid_user 
     flash[:error].should =~ /must be logged in/i 
     response.should redirect_to new_user_session_path 
     end 
    end 

    describe "as a non-admin user" do 
     it "should protect the page" do 
     sign_in(@valid_user) 
     delete :destroy, :id => @valid_user 
     response.should redirect_to(root_path) 
     flash[:error].should be_nil 
     flash[:success].should be_nil 
     flash[:notice].should be_nil    
     end 
    end 

    describe "as an admin user" do 

     before(:each) do 
     @admin = Factory(:user, :admin => true) 
     sign_in(@admin) 
     end 

     it "should destroy the user" do 
     lambda do 
      delete :destroy, :id => @valid_user 
     end.should change(User, :count).by(-1) 
     end 

     it "should redirect to the users page with the correct flash" do 
     delete :destroy, :id => @valid_user 
     response.should redirect_to(users_path) 
     flash[:success].should =~ /user destroyed/i 
     end 

    end 

    end 

end 

用户工厂:

Factory.define :valid_user , :class => User do |u| 
    u.name "brandon" 
    u.email "[email protected]" 
    u.password "foobar" 
    u.password_confirmation "foobar" 
end 

Factory.define :invalid_user , :class => User do |u| 
    u.name "" 
    u.email "[email protected]" 
    u.password "f" 
    u.password_confirmation "f" 
end 

Factory.sequence :email do |n| 
    "person#{n}@example.com" 
end 

Factory.sequence :name do |n| 
    "John Doe the #{n}" 
end 

Factory.define :user do |f| 

    f.name {Factory.next(:name)} 
    f.email {Factory.next(:email)} 
    f.password "foobar" 
    f.password_confirmation "foobar" 
end 

规格帮手

require 'database_cleaner' 
require 'spork' 
require 'factory_girl' 
require 'authlogic/test_case' 

Spork.prefork do 
    # Loading more in this block will cause your tests to run faster. However, 
    # if you change any configuration or code from libraries loaded here, you'll 
    # need to restart spork for it take effect. 

    def sign_in(user) 
     u = UserSession.create(user) 
    end 

    def sign_out 
     us = UserSession.find 
     us.destroy 
    end 

    def logged_in? 
     current_user_session = UserSession.find 
     return current_user_session.record if current_user_session 
     return false 
    end 

    DatabaseCleaner.strategy = :truncation 

    # This file is copied to spec/ when you run 'rails generate rspec:install' 
    ENV["RAILS_ENV"] ||= 'test' 
    require File.expand_path("../../config/environment", __FILE__) 
    require 'rspec/rails' 
    require 'rspec/autorun' 
    require 'webrat' 

    include Authlogic::TestCase 

    # Requires supporting ruby files with custom matchers and macros, etc, 
    # in spec/support/ and its subdirectories. 
    Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f} 

    Webrat.configure do |config| 
    config.mode = :rails 
    end 

    RSpec.configure do |config| 
     # == Mock Framework 
     # 
     # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line: 
     # 
     # config.mock_with :mocha 
     # config.mock_with :flexmock 
     # config.mock_with :rr 
     config.mock_with :rspec 

    ApplicationController.skip_before_filter :activate_authlogic 
    config.before(:each, :type => :request) do 
     activate_authlogic 
     #UserSession.create(User.find_by_email!(email)) 
    end 

     # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures 
     config.fixture_path = "#{::Rails.root}/spec/fixtures" 

     # If you're not using ActiveRecord, or you'd prefer not to run each of your 
     # examples within a transaction, remove the following line or assign false 
     # instead of true. 
     config.use_transactional_fixtures = true 

     # If true, the base class of anonymous controllers will be inferred 
     # automatically. This will be the default behavior in future versions of 
     # rspec-rails. 
     config.infer_base_class_for_anonymous_controllers = false 
    end 

end 

Spork.each_run do 
    # This code will be run each time you run your specs. 

    FactoryGirl.reload 
    ActiveSupport::Dependencies.clear 

    RSpec.configure do |config| 

    config.before(:each) do 
      DatabaseCleaner.start 
     end 

     config.after(:each) do 
      DatabaseCleaner.clean 
     end 

    end 

# DatabaseCleaner.clean 

end 

顺便说一句(有点,可能与我的其他问题),我不断收到这个令人沮丧的错误

16) UsersController GET edit success should render the 'edit' page 
Failure/Error: response.should render_template('edit') 
    expecting <"edit"> but rendering with <""> 
# ./spec/controllers/users_controller_spec.rb:203:in `block (4 levels) in <top (required)>' 

我明白任何可以提供的帮助 - 我在我的智慧结束!!我会发布你需要的任何附加代码。谢谢!

+0

请缩小你的问题。如果他们必须阅读这么多的代码才能做到这一点,人们不会很乐意回答你的问题。 – NullUserException

回答

3

你不叫setup :activate_authlogic

+0

哇,我以为这个早已不复存在......谢谢你的回答。我在我的spec_helper.rb文件中有以下代码:1.include Authlogic :: TestCase 2. config.before(:each)do,activate_authlogic,end(对于残酷的格式对不起,不要以为我可以把代码放在这里) – Brandon

+0

在“(:each)之前调用'activate_authlogic'没有效果。你必须把它叫做'setup'并在'describe'之后。 –