【问题标题】:Rails Tutorial Chapter 10 RSpec Failures by the end of chapterRails 教程第 10 章 RSpec 失败的章节末尾
【发布时间】:2012-01-23 10:17:04
【问题描述】:

我正在学习 Rails 教程并按照说明进行操作,我总是得到预期的结果,但在完成 chapter 10 后,我收到以下错误,我找不到问题所在。

1) UsersController PUT 'update' authentication of edit/update pages for non-signed-in users should deny access to 'edit'
 Failure/Error: @user = Factory(:user)
 ActiveRecord::RecordInvalid:
   Validation failed: Email has already been taken
 # ./spec/controllers/users_controller_spec.rb:259:in `block (4 levels) in <top (required)>'

2) UsersController PUT 'update' authentication of edit/update pages for non-signed-in users should deny access to 'update'
 Failure/Error: @user = Factory(:user)
 ActiveRecord::RecordInvalid:
   Validation failed: Email has already been taken
 # ./spec/controllers/users_controller_spec.rb:259:in `block (4 levels) in <top (required)>'

3) UsersController PUT 'update' authentication of edit/update pages for signed-in users should require matching users for 'edit'
 Failure/Error: @user = Factory(:user)
 ActiveRecord::RecordInvalid:
   Validation failed: Email has already been taken
 # ./spec/controllers/users_controller_spec.rb:259:in `block (4 levels) in <top (required)>'

4) UsersController PUT 'update' authentication of edit/update pages for signed-in users should require matching users for 'update'
 Failure/Error: @user = Factory(:user)
 ActiveRecord::RecordInvalid:
   Validation failed: Email has already been taken
 # ./spec/controllers/users_controller_spec.rb:259:in `block (4 levels) in <top (required)>'

5) UsersController DELETE 'destroy' as a non-signed-in user should deny access
 Failure/Error: delete :destroy, :id => @user
 NoMethodError:
   undefined method `admin?' for nil:NilClass
 # ./app/controllers/users_controller.rb:68:in `admin_user'
 # ./spec/controllers/users_controller_spec.rb:303:in `block (4 levels) in <top (required)>'

应用程序运行良好,功能已完成,但测试未通过。起初我以为可能有错别字,然后我仔细检查是否漏掉了什么,但没有运气......

作为参考,这是我的 RSpec 测试:

describe "PUT 'update'" do

before(:each) do
  @user = Factory(:user)
  test_sign_in(@user)
end

describe "failure" do

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

  it "should render the 'edit' page" do
    put :update, :id => @user, :user => @attr
    response.should render_template('edit')
  end

  it "should have the right title" do
    put :update, :id => @user, :user => @attr
    response.should have_selector("title", :content => "Edit user")
  end
end

describe "success" do

  before(:each) do
    @attr = { :name => "New Name", :email => "user@example.org",
              :password => "barbaz", :password_confirmation => "barbaz" }
  end

  it "should change the user's attributes" do
    put :update, :id => @user, :user => @attr
    @user.reload
    @user.name.should  == @attr[:name]
    @user.email.should == @attr[:email]
  end

  it "should redirect to the user show page" do
    put :update, :id => @user, :user => @attr
    response.should redirect_to(user_path(@user))
  end

  it "should have a flash message" do
    put :update, :id => @user, :user => @attr
    flash[:success].should =~ /updated/
  end
end


describe "authentication of edit/update pages" do

before(:each) do
  @user = Factory(:user)
end

describe "for non-signed-in users" do

  it "should deny access to 'edit'" do
    get :edit, :id => @user
    response.should redirect_to(signin_path)
  end

  it "should deny access to 'update'" do
    put :update, :id => @user, :user => {}
    response.should redirect_to(signin_path)
  end
end

describe "for signed-in users" do

    before(:each) do
      wrong_user = Factory(:user, :email => "user@example.net")
      test_sign_in(wrong_user)
    end

    it "should require matching users for 'edit'" do
      get :edit, :id => @user
      response.should redirect_to(root_path)
    end

    it "should require matching users for 'update'" do
      put :update, :id => @user, :user => {}
      response.should redirect_to(root_path)
    end
  end
end
  end

  describe "DELETE 'destroy'" do

before(:each) do
  @user = Factory(:user)
end

describe "as a non-signed-in user" do
  it "should deny access" do
    delete :destroy, :id => @user
    response.should redirect_to(signin_path)
  end
end

describe "as a non-admin user" do
  it "should protect the page" do
    test_sign_in(@user)
    delete :destroy, :id => @user
    response.should redirect_to(root_path)
  end
end

describe "as an admin user" do

  before(:each) do
    admin = Factory(:user, :email => "admin@example.com", :admin => true)
    test_sign_in(admin)
  end

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

  it "should redirect to the users page" do
    delete :destroy, :id => @user
    response.should redirect_to(users_path)
  end
end
end

还有我的用户控制器方法:

def update
@user = User.find(params[:id])
if @user.update_attributes(params[:user])
  flash[:success] = "Profile updated."
  redirect_to @user
else
  @title = "Edit user"
  render 'edit'
end
end

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

这是我的工厂.rb

# By using the symbol ':user', we get Factory Girl to simulate the User model.
Factory.define :user do |user|
  user.name                  "Michael Hartl"
  user.email                 "mhartl@example.com"
  user.password              "foobar"
  user.password_confirmation "foobar"
end

Factory.sequence :name do |n|
  "Person #{n}"
end

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

任何帮助将不胜感激!

【问题讨论】:

    标签: ruby-on-rails rspec railstutorial.org


    【解决方案1】:

    据我所知,运行测试后您的表没有被清除。您确定您的测试在退出时清除了表格吗?通常使用 Database Cleaner gem 来执行此操作。

    问题似乎出在这行代码上:

    @user = Factory(:user)
    

    在规范的这一部分:

    describe "authentication of edit/update pages" do
      before(:each) do
        @user = Factory(:user)
      end
    

    试试改成

    @user = Factory(:user, :email => "foo@bar.com")
    

    或者完全删除它。

    在我看来,它嵌套在这个父规范下:

    describe "PUT 'update'" do
      before(:each) do
        @user = Factory(:user)
        test_sign_in(@user)
      end
    

    在这里,在运行 "authentication of edit/update pages" 规范之前创建了具有相同电子邮件的用户模型,当在 "authentication of edit/update pages" 中创建用户时,规范失败,因为 email 应该是唯一的。

    更新:

    我查看了您的规范,看起来您忘记在正确的位置关闭一个块(并且end 关键字放错了位置)。

    这是正确的规范:

    require 'spec_helper'
    
    describe UsersController do
      render_views
    
      describe "GET 'index'" do
    
        describe "for non-signed-in users" do
          it "should deny access" do
            get :index
            response.should redirect_to(signin_path)
            flash[:notice].should =~ /sign in/i
          end
        end
    
        describe "for signed-in users" do
    
          before(:each) do
            @user = test_sign_in(Factory(:user))
            second = Factory(:user, :name => "Bob", :email => "another@example.com")
            third = Factory(:user, :name => "Ben", :email => "another@example.net")
    
            @users = [@user, second, third]
            30.times do
              @users << Factory(:user, :name => Factory.next(:name),
                                :email => Factory.next(:email))
            end
          end
    
          it "should be successful" do
            get :index
            response.should be_success
          end
    
          it "should have the right title" do
            get :index
            response.should have_selector("title", :content => "All users")
          end
    
          it "should have an element for each user" do
            get :index
            @users[0..2].each do |user|
              response.should have_selector("li", :content => user.name)
            end
          end
    
          it "should paginate users" do
            get :index
            response.should have_selector("div.pagination")
            response.should have_selector("span.disabled", :content => "Previous")
            response.should have_selector("a", :href => "/users?page=2",
                                          :content => "2")
            response.should have_selector("a", :href => "/users?page=2",
                                          :content => "Next")
          end
        end
      end
    
      describe "GET 'show'" do
    
        before(:each) do
          @user = Factory(:user)
        end
    
        it "should be successful" do
          get :show, :id => @user
          response.should be_success
        end
    
        it "should find the right user" do
          get :show, :id => @user
          assigns(:user).should == @user
        end
    
        it "should have the right title" do
          get :show, :id => @user
          response.should have_selector("title", :content => @user.name)
        end
    
        it "should include the user's name" do
          get :show, :id => @user
          response.should have_selector("h1", :content => @user.name)
        end
    
        it "should have a profile image" do
          get :show, :id => @user
          response.should have_selector("h1>img", :class => "gravatar")
        end
      end
    
      describe "GET 'new'" do
        it "should be successful" do
          get 'new'
          response.should be_success
        end
    
        it "should have the right title" do
          get 'new'
          response.should have_selector("title", :content => "Sign up")
        end
    
        it "should have a name field" do
          get :new
          response.should have_selector("input[name='user[name]'][type='text']")
        end
    
        it "should have an email field" do
          get :new
          response.should have_selector("input[name='user[email]'][type='text']")
        end
    
        it "should have a password field" do
          get :new
          response.should have_selector("input[name='user[password]'][type='password']")
        end
    
        it "should have a password confirmation field" do
          get :new
          response.should have_selector("input[name='user[password_confirmation]'][type='password']")
        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 have the right title" do
            post :create, :user => @attr
            response.should have_selector("title", :content => "Sign up")
          end
    
          it "should render the 'new' page" do
            post :create, :user => @attr
            response.should render_template('new')
          end
        end
    
    
        describe "success" do
    
          before(:each) do
            @attr = {:name => "New User", :email => "user@example.com",
                     :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 sign the user in" do
            post :create, :user => @attr
            controller.should be_signed_in
          end
    
          it "should redirect to the user show page" do
            post :create, :user => @attr
            response.should redirect_to(user_path(assigns(:user)))
          end
    
          it "should have a welcome message" do
            post :create, :user => @attr
            flash[:success].should =~ /welcome to the sample app/i
          end
        end
      end
    
      describe "GET 'edit'" do
    
        before(:each) do
          @user = Factory(:user)
          test_sign_in(@user)
        end
    
        it "should be successful" do
          get :edit, :id => @user
          response.should be_success
        end
    
        it "should have the right title" do
          get :edit, :id => @user
          response.should have_selector("title", :content => "Edit user")
        end
    
        it "should have a link to change the Gravatar" do
          get :edit, :id => @user
          gravatar_url = "http://gravatar.com/emails"
          response.should have_selector("a", :href => gravatar_url,
                                        :content => "change")
        end
      end
    
      describe "PUT 'update'" do
        before(:each) do
          @user = Factory(:user)
          test_sign_in(@user)
        end
    
        describe "failure" do
          before(:each) do
            @attr = {:email => "", :name => "", :password => "",
                     :password_confirmation => ""}
          end
    
          it "should render the 'edit' page" do
            put :update, :id => @user, :user => @attr
            response.should render_template('edit')
          end
    
          it "should have the right title" do
            put :update, :id => @user, :user => @attr
            response.should have_selector("title", :content => "Edit user")
          end
        end
    
        describe "success" do
          before(:each) do
            @attr = {:name => "New Name", :email => "user@example.org",
                     :password => "barbaz", :password_confirmation => "barbaz"}
          end
    
          it "should change the user's attributes" do
            put :update, :id => @user, :user => @attr
            @user.reload
            @user.name.should == @attr[:name]
            @user.email.should == @attr[:email]
          end
    
          it "should redirect to the user show page" do
            put :update, :id => @user, :user => @attr
            response.should redirect_to(user_path(@user))
          end
    
          it "should have a flash message" do
            put :update, :id => @user, :user => @attr
            flash[:success].should =~ /updated/
          end
        end
      end
    
      describe "authentication of edit/update pages" do
        before(:each) do
          @user = Factory(:user)
        end
    
        describe "for non-signed-in users" do
    
          it "should deny access to 'edit'" do
            get :edit, :id => @user
            response.should redirect_to(signin_path)
          end
    
          it "should deny access to 'update'" do
            put :update, :id => @user, :user => {}
            response.should redirect_to(signin_path)
          end
        end
    
        describe "for signed-in users" do
    
          before(:each) do
            wrong_user = Factory(:user, :email => "user@example.net")
            test_sign_in(wrong_user)
          end
    
          it "should require matching users for 'edit'" do
            get :edit, :id => @user
            response.should redirect_to(root_path)
          end
    
          it "should require matching users for 'update'" do
            put :update, :id => @user, :user => {}
            response.should redirect_to(root_path)
          end
        end
      end
    
      describe "DELETE 'destroy'" do
    
        before(:each) do
          @user = Factory(:user)
        end
    
        describe "as a non-signed-in user" do
          it "should deny access" do
            delete :destroy, :id => @user
            response.should redirect_to(signin_path)
          end
        end
    
        describe "as a non-admin user" do
          it "should protect the page" do
            test_sign_in(@user)
            delete :destroy, :id => @user
            response.should redirect_to(root_path)
          end
        end
    
        describe "as an admin user" do
    
          before(:each) do
            admin = Factory(:user, :email => "admin@example.com", :admin => true)
            test_sign_in(admin)
          end
    
          it "should destroy the user" do
            lambda do
              delete :destroy, :id => @user
            end.should change(User, :count).by(-1)
          end
    
          it "should redirect to the users page" do
            delete :destroy, :id => @user
            response.should redirect_to(users_path)
          end
        end
      end
    end
    

    然而,在这个更正的规范中,UsersController DELETE 'destroy' as a non-signed-in user should deny access 规范失败了,因为实现不正确(它假设 current_user 永远不会返回 nil,但它有时会返回)。

    更新 2: 可以看到使所有测试通过所需的所有更改here

    除了缩进修复之外,唯一需要修复的是UsersController 类中的admin_user 回调:

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

    【讨论】:

    • 我不认为我已经实现了。你能解释一下为什么有必要吗?
    • 我检查过,每次测试后数据库都会被清理。
    • 您可以压缩您的项目并将其上传到某个地方,我会下载它并查看。
    • 我刚刚更新了问题并将 factory.rb 文件放在了上面。关键是有很多用户。我感谢您的帮助!您可以在github.com/svjozi/sample_app 找到该应用程序
    • 您好,请查看更新后的答案。正如我在更新的答案中提到的那样,其中一个规范应该失败,因为实现实际上是不正确的。
    【解决方案2】:

    我得到了同样的错误,因为我忘了准备测试数据库:

    rake db:test:prepare
    

    【讨论】:

      【解决方案3】:

      Listing 10.36 中,您是否粘贴了整个代码块以在其中创建管理员用户,而没有删除原始块?我在教程的那个时候遇到了类似的问题,这就是罪魁祸首。

      应该是这样的:

      lib/tasks/sample_data.rake

      namespace :db do
        desc "Fill database with sample data"
        task :populate => :environment do
          Rake::Task['db:reset'].invoke
          admin = User.create!(:name => "Example User",
                           :email => "example@railstutorial.org",
                           :password => "foobar",
                           :password_confirmation => "foobar")
          admin.toggle!(:admin)
          99.times do |n|
            name  = Faker::Name.name
            email = "example-#{n+1}@railstutorial.org"
            password  = "password"
            User.create!(:name => name,
                     :email => email,
                     :password => password,
                     :password_confirmation => password)
          end
        end
      end
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-06-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多