测试RoR教程失败(第9.4章)

时间:2014-03-29 08:58:52

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

我正在参加RoR教程(第9.4章)。当所有的rspec测试都变成绿色时,我就无法让我的工作了。我运行bundle exec rspec spec /并得到四个失败,出现此错误:

Failures:

  1) User pages index signup page with valid information after saving the user
     Failure/Error: sign_in user
     NoMethodError:
       undefined method `email' for nil:NilClass
     # ./spec/support/utilities.rb:23:in `sign_in'
     # ./spec/requests/user_pages_spec.rb:12:in `block (3 levels) in <top (requi
red)>'

  2) User pages index signup page with valid information after saving the user
     Failure/Error: sign_in user
     NoMethodError:
       undefined method `email' for nil:NilClass
     # ./spec/support/utilities.rb:23:in `sign_in'
     # ./spec/requests/user_pages_spec.rb:12:in `block (3 levels) in <top (requi
red)>'

  3) User pages index signup page with valid information after saving the user
     Failure/Error: sign_in user
     NoMethodError:
       undefined method `email' for nil:NilClass
     # ./spec/support/utilities.rb:23:in `sign_in'
     # ./spec/requests/user_pages_spec.rb:12:in `block (3 levels) in <top (requi
red)>'

  4) Authentication signin with invalid information after visiting another page

     Failure/Error: it { should_not have_selector('div.alert.alert-error') }
       expected #has_selector?("div.alert.alert-error") to return false, got tru
e
     # ./spec/requests/authentication_pages_spec.rb:26:in `block (5 levels) in <
top (required)>'

Finished in 21.33 seconds
81 examples, 4 failures

utilities.rb:

include ApplicationHelper

def valid_signin(user)
  fill_in "Email",    with: user.email
  fill_in "Password", with: user.password
  click_button "Sign in"
end

RSpec::Matchers.define :have_error_message do |message|
  match do |page|
    expect(page).to have_selector('div.alert.alert-error', text: message)
  end
end

def sign_in(user, options={})
  if options[:no_capybara]
    # Sign in when not using Capybara.
    remember_token = User.new_remember_token
    cookies[:remember_token] = remember_token
    user.update_attribute(:remember_token, User.encrypt(remember_token))
  else
    visit signin_path
    fill_in "Email",    with: user.email
    fill_in "Password", with: user.password
    click_button "Sign in"
  end
end

user_page_spec.rb:

require 'spec_helper'

describe "User pages" do

  subject { page }

  describe "index" do

    let(:user) { FactoryGirl.create(:user) }

    before do
      sign_in user
      visit users_path
    end

    it { should have_title('All users') }
    it { should have_content('All users') }

    describe "pagination" do

      before(:all) { 30.times { FactoryGirl.create(:user) } }
      after(:all)  { User.delete_all }

      it { should have_selector('div.pagination') }

      it "should list each user" do
        User.paginate(page: 1).each do |user|
          expect(page).to have_selector('li', text: user.name)
        end
      end
    end

   describe "delete links" do

      it { should_not have_link('delete') }

      describe "delete links" do

      it { should_not have_link('delete') }

      describe "as an admin user" do
        let(:admin) { FactoryGirl.create(:admin) }
        before do
          sign_in admin
          visit users_path
        end

        it { should have_link('delete', href: user_path(User.first)) }
        it "should be able to delete another user" do
          expect do
            click_link('delete', match: :first)
          end.to change(User, :count).by(-1)
        end
        it { should_not have_link('delete', href: user_path(admin)) }
      end
    end
  end


  describe "profile page" do
    let(:user) { FactoryGirl.create(:user) }
    before { visit user_path(user) }

    it { should have_content(user.name) }
    it { should have_title(user.name) }
  end

    describe "signup page" do

    before { visit signup_path }

    let(:submit) { "Create my account" }

    describe "with invalid information" do
      it "should not create a user" do
        expect { click_button submit }.not_to change(User, :count)
      end
    end

    describe "with valid information" do
      before do
        fill_in "Name",         with: "Example User"
        fill_in "Email",        with: "user@example.com"
        fill_in "Password",     with: "foobar"
        fill_in "Confirmation", with: "foobar"
      end

      it "should create a user" do
        expect { click_button submit }.to change(User, :count).by(1)
      end

      describe "after saving the user" do
        before { click_button submit }
        let(:user) { User.find_by(email: 'user@example.com') }

        it { should have_link('Sign out') }
        it { should have_title(user.name) }
        it { should have_selector('div.alert.alert-success', text: 'Welcome') }
      end

    end
  end

    describe "edit" do
    let(:user) { FactoryGirl.create(:user) }
    before do
      sign_in user
      visit edit_user_path(user)
    end

    describe "page" do
      it { should have_content("Update your profile") }
      it { should have_title("Edit user") }
      it { should have_link('change', href: 'http://gravatar.com/emails') }
    end

    describe "with valid information" do
      let(:new_name)  { "New Name" }
      let(:new_email) { "new@example.com" }
      before do
        fill_in "Name",             with: new_name
        fill_in "Email",            with: new_email
        fill_in "Password",         with: user.password
        fill_in "Confirm Password", with: user.password
        click_button "Save changes"
      end

      it { should have_title(new_name) }
      it { should have_selector('div.alert.alert-success') }
      it { should have_link('Sign out', href: signout_path) }
      specify { expect(user.reload.name).to  eq new_name }
      specify { expect(user.reload.email).to eq new_email }
    end
    end
end
end

authentication_pages_spec.rb:

require 'spec_helper'

describe "Authentication" do

  subject { page }

  describe "signin page" do
    before { visit signin_path }

    it { should have_content('Sign in') }
    it { should have_title('Sign in') }
  end

describe "signin" do

    before { visit signin_path }

    describe "with invalid information" do
      before { click_button "Sign in" }

      it { should have_title('Sign in') }
      it { should have_selector('div.alert.alert-error') }

      describe "after visiting another page" do
        before { click_link "Home" }
        it { should_not have_selector('div.alert.alert-error') }
      end
    end


  describe "with valid information" do
      let(:user) { FactoryGirl.create(:user) }
      before { sign_in user }

      it { should have_title(user.name) }
      it { should have_link('Users',       href: users_path) }
      it { should have_link('Profile',     href: user_path(user)) }
      it { should have_link('Settings',    href: edit_user_path(user)) }
      it { should have_link('Sign out',    href: signout_path) }
      it { should_not have_link('Sign in', href: signin_path) }



    describe "follower by signout" do
      before {click_link "Sign out"}
      it {should have_link('Sign in')}
      end
    end
  end

 describe "authorization" do

    describe "for non-signed-in users" do
      let(:user) { FactoryGirl.create(:user) }

      describe "when attempting to visit a protected page" do
        before do
          visit edit_user_path(user)
          fill_in "Email", with: user.email
          fill_in "Password", with: user.password
          click_button "Sign in"
        end

        describe "after signing in" do

          it "should render the desired protected page" do
            expect(page).to have_title('Edit user')
          end
        end
      end

      describe "in the Users controller" do

        describe "visiting the edit page" do
          before { visit edit_user_path(user) }
          it { should have_title('Sign in') }
        end


        describe "submitting to the update action" do
          before { patch user_path(user) }
          specify { expect(response).to redirect_to(signin_path) }
        end
      end
        describe "visiting the user index" do
          before { visit users_path }
          it { should have_title('Sign in') }
        end

    end

    describe "as wrong user" do
      let(:user) { FactoryGirl.create(:user) }
      let(:wrong_user) { FactoryGirl.create(:user, email: "wrong@example.com") }
      before { sign_in user, no_capybara: true }

      describe "submitting a GET request to the Users#edit action" do
        before { get edit_user_path(wrong_user) }
        specify { expect(response.body).not_to match(full_title('Edit user')) }
        specify { expect(response).to redirect_to(root_url) }
      end

      describe "submitting a PATCH request to the Users#update action" do
        before { patch user_path(wrong_user) }
        specify { expect(response).to redirect_to(root_url) }
      end
    end

   describe "as non-admin user" do
      let(:user) { FactoryGirl.create(:user) }
      let(:non_admin) { FactoryGirl.create(:user) }

      before { sign_in non_admin, no_capybara: true }

      describe "submitting a DELETE request to the Users#destroy action" do
        before { delete user_path(user) }
        specify { expect(response).to redirect_to(root_url) }
      end
    end
  end
end

user.rb

class User < ActiveRecord::Base
  has_secure_password
  before_save { self.email = email.downcase }
  before_create :create_remember_token
  validates :name, presence: true, length: { maximum: 50 }
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i
  validates :email, presence: true, format: { with: VALID_EMAIL_REGEX },
                    uniqueness: { case_sensitive: false }
  has_secure_password
  validates :password, length: { minimum: 6 }

  def User.new_remember_token
    SecureRandom.urlsafe_base64
  end

  def User.encrypt(token)
    Digest::SHA1.hexdigest(token.to_s)
  end

  private

    def create_remember_token
      self.remember_token = User.encrypt(User.new_remember_token)
    end

end

factories.rb:

FactoryGirl.define do
  factory :user do
    sequence(:name)  { |n| "Person #{n}" }
    sequence(:email) { |n| "person_#{n}@example.com"}
    password "foobar"
    password_confirmation "foobar"

    factory :admin do
      admin true
    end
  end
end

rails console test:

irb(main):001:0> **user = FactoryGirl.build(:user)**
=> #<User id: nil, name: "Person 1", email: "person_1@example.com", created_at:
nil, updated_at: nil, password_digest: "$2a$04$LiRdaarU6QaX9PJa1uFjE.5e44SYRsmy1
OMY8A4EKZow...", remember_token: nil, admin: false>

irb(main):002:0> **user.valid?**
  User Exists (1.0ms)  SELECT 1 AS one FROM "users" WHERE LOWER("users"."email")
 = LOWER('person_1@example.com') LIMIT 1
=> true

irb(main):003:0> **user.errors**
=> #<ActiveModel::Errors:0x49dd678 @base=#<User id: nil, name: "Person 1", email
: "person_1@example.com", created_at: nil, updated_at: nil, password_digest: "$2
a$04$LiRdaarU6QaX9PJa1uFjE.5e44SYRsmy1OMY8A4EKZow...", remember_token: nil, admi
n: false>, @messages={}>

我更改了users_controller.rb,现在我只有3(1-3)个失败

users_controller.rb:

          class UsersController < ApplicationController
  before_action :signed_in_user, only: [:index, :edit, :update, :destroy]
  before_action :correct_user,   only: [:edit, :update]
  before_action :admin_user,     only: :destroy

  def index
    @users = User.paginate(page: params[:page])
  end

  def show
    @user = User.find(params[:id])
  end

  def new
    @user = User.new
  end

   def create
    @user = User.new(user_params)
    if @user.save
      sign_in @user
      flash[:success] = "Welcome to the Sample App!"
      redirect_to @user
    else
      render 'new'
    end
  end

   def edit
   end

  def update
    if @user.update_attributes(user_params)
      flash[:success] = "Profile updated"
      redirect_to @user 
      # Handle a successful update.
    else
      render 'edit'
    end
  end

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

  private

  def user_params
      params.require(:user).permit(:name, :email, :password,
                                   :password_confirmation)
    end

    # Before filters

    def signed_in_user
      unless signed_in?
        store_location
        redirect_to signin_url, notice: "Please sign in."
      end
    end


    def correct_user
      @user = User.find(params[:id])
      redirect_to(root_url) unless current_user?(@user)
    end


  def admin_user
      redirect_to(root_url) unless current_user.admin?
    end

end

实际项目版本 - https://tranquil-waters-6116.herokuapp.com。也许它有助于找到失败的原因/

2 个答案:

答案 0 :(得分:1)

end中的一个user_page_spec.rb语句是错误的。 describe "index"块应在describe "profile page"之前结束。

describe "index" do
  ...
  describe "pagination" do
    ...
  end

  describe "delete links" do
    ...
  end
end

describe "profile page" do
  ...
end

describe "signup page" do
  ...
end 

etc.

注册是为新用户注册的,因此测试不应该首先让用户登录。

顺便说一句,你也有describe "delete links"两次,嵌套在自己内部,虽然这不会是错误的原因;它只是重复一次测试。

答案 1 :(得分:1)

如果我们能够看到您factory :userspec/factories.rb的定义方式也会很好,因为您已经以不同的方式定义了VALID_EMAIL_REGEX。

对于初学者,您可以将正则表达式条件恢复为原始状态:

  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i

确保验证部分与您的factory :user一起使用。

并删除第二个has_secure_password以确保安全。