Rails Tutorial Глава 10 Ошибки RSpec к концу главы - PullRequest
1 голос
/ 23 января 2012

Я работаю над учебником по Rails и, следуя инструкциям, всегда получаю ожидаемые результаты, но после окончания главы 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

Любая помощь будет принята с благодарностью !!

Ответы [ 3 ]

2 голосов
/ 29 ноября 2012

Я получил ту же ошибку, потому что я забыл подготовить тестовую базу данных:

rake db:test:prepare
2 голосов
/ 23 января 2012

Насколько я вижу, ваши таблицы не очищаются после запуска тестов. Вы уверены, что ваши тесты очищают таблицу при выходе? Обычно для этого используется гем Database Cleaner.

Кажется, проблема в этой строке кода:

@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 должен быть уникальным.

UPDATE:

Я посмотрел вашу спецификацию и выглядело так, как будто вы забыли закрыть блок в нужном месте (и ключевое слово 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: Все изменения, необходимые для прохождения всех тестов, можно увидеть здесь .

Кроме исправлений отступов, единственное, что нужно было исправить, это обратный вызов admin_user в классе UsersController:

 def admin_user
    redirect_to(root_path) unless (current_user && current_user.admin?)
 end
0 голосов
/ 23 января 2012

В Листинг 10.36 , вставили ли вы весь блок кода для создания пользователя-администратора, не удаляя исходный блок? В тот момент в уроке у меня была похожая проблема, и это было причиной.

Должно выглядеть так:

Библиотека / Задачи / 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
...