controller rspec不会从数据库重新加载current_user或@user

时间:2013-09-25 14:31:50

标签: ruby-on-rails rspec devise controller

FEATURE

用户有个人资料,应该可以更新。


问题

我更新配置文件,例如将名称更改为“Homer Simpson”,但所有断言都失败,因为数据库记录似乎没有更新。

我似乎无法获得更新的属性:

 Failure/Error: expect(subject.current_user.first_name).to eq('Homer')

   expected: "Homer"
        got: "Lew"

   (compared using ==)
 # ./spec/controllers/registrations_controller_spec.rb:67:in `block (3 levels) in <top (required)>'

N.B。我试过了@user.reloadsubject.current_user.reload

规格仍未通过。


CODE

我正在使用:

  • rails(4.0.0)
  • 设计(3.0.3)
  • rspec-rails(2.14.0)
  • capybara(2.1.0)
  • factory_girl(4.2.0)
  • database_cleaner(1.1.1)

我已经查过:

registrations_controller_spec.rb

describe "User Profiles" do
  login_user

  it "Update - changes the user's attributes" do
    put :update, id: @user, user: attributes_for(:user, first_name: 'Homer')
    @user.reload
    expect(@user.first_name).to eq('Homer') # FAILS
  end
end

我尝试在此Stackoverflow主题中更换@user subject.current_user"Devise Rspec registration controller test failing on update as if it was trying to confirm email address"

  put :update, id: subject.current_user, user: attributes_for(:user, first_name: 'Homer')
  subject.current_user.reload
  expect(subject.current_user.first_name).to eq('Homer') # Still FAILS

但它仍然失败。

控制器中有问题吗?我按current_user.id而不是params[:id]找到了用户。

registrations_controller.rb

def update
  @user = User.find(current_user.id)
  email_changed = @user.email != params[:user][:email]
  password_changed = !params[:user][:password].blank?

  if email_changed or password_changed
    successfully_updated = @user.update_with_password(user_params)
  else
    successfully_updated = @user.update_without_password(user_params)
  end

  if successfully_updated
    sign_in @user, bypass: true # Sign in the user bypassing validation in case his password changed
    redirect_to user_profile_path, notice: 'Profile was successfully updated.'
  else
    render "edit"
  end
end

controller_macros.rb - 定义login_user帮助

module ControllerMacros
  def login_user    
    before(:each) do
      @request.env["devise.mapping"] = Devise.mappings[:user]
      @user = FactoryGirl.create(:user)
      @user.confirm!
      sign_in @user
    end
  end
end

我的集成规格很好。我在控制器中缺少什么?

3 个答案:

答案 0 :(得分:2)

我的回答可以解决您的问题,但不能直接修复代码中的错误。要做到这一点,我需要编写更多测试和动手调试,我只是通过阅读来解决这个问题并不是很有经验:)

我建议你不要覆盖Devise的RegistrationsController。与原始代码相比,您的代码至少缺少两件事:

  1. 没有current_user对象的副本。在真实应用中,current_user将通过提交不合适的表单进行注销。

  2. 缺乏参数消毒

  3. 剩下的错误。

    我的建议是直接使用Devise的方法,因为您的代码中没有没有特殊,也无需覆盖完整代码

    class RegistrationsController < Devise::RegistrationsController
      def update
      end
      # Or even without this method.
    end
    

    就是这样。

    不需要密码

    def update
      params.merge!(password: current_user.password) if params[:password].blank?
      super
    end
    

    对于测试,只需编写一些临时集成测试。 Devise有完整的涵盖功能测试,因此无需重复。

答案 1 :(得分:0)

尝试assigns

it "Update - changes the user's attributes" do
  put :update, id: @user, user: attributes_for(:user, first_name: 'Homer')
  homer = assigns(:user)
  @user.reload
  expect(homer.first_name).to eq('Homer')
end

更新:根据Billy Chan的评论,这应该正确测试该名称是否正在更新

it "Update - changes the user's attributes" do
  put :update, id: @user, user: attributes_for(:user, first_name: 'Homer')
  homer = assigns(:user)
  @user.reload
  #expect(homer.first_name).to eq('Homer') Peter and Billy are right, this only tests
  # that the attribute was actually assigned, not that the update was successful
  expect(@user.first_name).to eq(homer.first_name)
  #however this test that the users updated `first_name` matches the attribute 
  #in the test 
end

注意

我的答案基于几个月前我经历过的Michael Hartl教程 - 他使用这种方法,我相信他解释了原因 - 虽然我没有屏幕投射在我面前时刻。我稍后再仔细查看。

视频

Here's the video - 它的质量非常低,因为我只是使用了quicktime的屏幕记录 - 并且在开始时有一些残酷的反馈循环,因此在最初几秒钟静音您的计算机。

答案 2 :(得分:0)

答案

感谢大家的建议,这些建议帮助我了解了我的代码并找到了问题。

失败原因:默认出厂时包含电子邮件和密码,因此控制器测试一直试图更改用户密码。

具体而言,我在 registrations_controller_spec.rb

中更改了这行代码
put :update, id: @user, user: attributes_for(:user, first_name: 'Homer')

为:

patch :update, id: @user, user: attributes_for(:user_params, first_name: 'Homer', last_name: 'Simpson')

然后我不得不更新我的工厂,因此我可以使用 :user_params 代替更新:

FactoryGirl.define do

  factory :user do
    first_name          { Faker::Name.first_name }
    last_name           { Faker::Name.last_name }
    sequence(:username) { |n| "user-#{n}" }
    email               { Faker::Internet.email }
    password            { Faker::Lorem.characters(8) }
  end

  factory :user_params, class: :user do
    first_name     { Faker::Name.first_name }
    last_name      { Faker::Name.last_name }

    factory :user_params_with_email, class: :user do
      email        { Faker::Internet.email }
    end

    factory :user_params_with_password, class: :user do
      password    { Faker::Lorem.characters(8) }
    end
  end

end

感谢所有提出建议的人。它帮助我解开了我的代码,@ billy-chan指出问题是对的,我修复了。

  • Params没有消毒(我正在完成rails4升级)
  • 其他misc。虫子

经验教训

比较进出控制器的参数。

我的集成测试正在通过,因为我没有尝试更改电子邮件或密码。