Vahid
Vahid

Reputation: 11

Rails Tutorial Chapter 10 RSpec Failures by the end of chapter

I'm working on the Rails tutorial and as follow the instructions, I always get the results expected but after finishing chapter 10, I get the bellow errors and I can't find what it the issue is.

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)>'

the app runs well and the functionalities are done but the tests don't pass. Initially I thought there might be typos and then I double checked to see if I missed something but there was not luck...

For reference this is my RSpec test:

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 => "[email protected]",
              :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 => "[email protected]")
      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 => "[email protected]", :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

And my user controller methods:

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

Here is my factories.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                 "[email protected]"
  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

Any help would be GREATLY appreciated!!

Upvotes: 1

Views: 1320

Answers (3)

Grey
Grey

Reputation: 676

I got the same error, because I forgot to prepare the test database:

rake db:test:prepare

Upvotes: 2

Behrang Saeedzadeh
Behrang Saeedzadeh

Reputation: 47971

As far as I can see your tables are not being cleared after the tests are run. Are you sure your tests clear the table at exit? Usually the Database Cleaner gem is used to do this.

The problem appears to be with this line of code:

@user = Factory(:user)

in this part of your spec:

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

Try changing it to

@user = Factory(:user, :email => "[email protected]")

Or remove it completely.

It seems to me that it's nested under this parent spec:

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

And as here a user model with the same email is created before running the "authentication of edit/update pages" specs, when in "authentication of edit/update pages" you create the user the spec fails because email should be unique.

UPDATE:

I had a look at your spec and looks like you had forgotten to close a block in the right place (and end keyword was misplaced).

This is the correct spec:

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 => "[email protected]")
        third = Factory(:user, :name => "Ben", :email => "[email protected]")

        @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 => "[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 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 => "[email protected]",
                 :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 => "[email protected]")
        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 => "[email protected]", :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

However in this corrected spec, the UsersController DELETE 'destroy' as a non-signed-in user should deny access spec fails, because the implementation is incorrect (it assumes current_user will never return nil but it sometimes does).

UPDATE 2: All the changes that are necessary to make all the tests pass can be seen here.

Other than indentation fixes the only thing that needed to be fixed was the admin_user callback in the UsersController class:

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

Upvotes: 2

ellawren
ellawren

Reputation: 947

In Listing 10.36, did you paste the entire block of code to create the admin user in, without removing the original block? I had a similar problem at that point in the tutorial and that was the culprit.

Should look like this:

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 => "[email protected]",
                     :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

Upvotes: 0

Related Questions