【问题标题】:controller rspec doesn't reload current_user or @user from database控制器 rspec 不会从数据库重新加载 current_user 或 @user
【发布时间】:2013-09-25 14:31:50
【问题描述】:

特点

用户拥有个人资料并且应该能够对其进行更新。


问题

我更新了配置文件,例如将名称更改为“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)>'

注意@user.reloadsubject.current_user.reload我都试过了

规范仍未通过。


代码

我正在使用:

  • 导轨 (4.0.0)
  • 设计 (3.0.3)
  • rspec-rails (2.14.0)
  • 水豚 (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

我尝试将@user 换成subject.current_user,就像在这个 Stackoverflow 线程中一样:"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 helper

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

我的集成规范通过了。我在控制器中缺少什么?

【问题讨论】:

    标签: ruby-on-rails rspec devise controller


    【解决方案1】:

    我的回答可以解决您的问题,但不能直接修复您代码中的错误。为此,我需要编写更多的测试和动手调试,我没有那么多经验来通过只读来解决它:)

    我不建议您像有问题的那样覆盖 Devise 的 RegistrationsController。与原始代码相比,您的代码至少缺少两点:

    1. 没有 current_user 对象的副本。在实际应用中,current_user 将通过提交不好的表单来注销。

    2. 缺乏清理参数

    还有剩下的错误。

    我的建议是直接使用Devise的方法,因为你的代码中没什么特别的,不需要重写完整的代码。 p>

    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 具有全面的功能测试,因此无需重复。

    【讨论】:

    • 对不起,我不同意。使用默认更新不是一个选项。我想allow users to edit accounts without providing passwords - as per the devise wiki
    • 正如我在原帖中所说,我的集成规范过去了。我真的在问这个问题,试图理解问题,即 rspec 并设计得更好。到目前为止,我还不是更聪明。 (对不起,我在上面的评论中输入得太早了)
    • jng5,我不明白为什么您的集成测试通过了,因为显然用户对象在此控制器规范中更新失败。对于 Wiki 部分,我无法发表评论,因为我没有使用那个 sn-p,但是 sn-p 看起来已经过时了,因为它与当前的代码相差甚远。
    • jng5,不需要密码覆盖的版本:)
    • 功能按预期工作(当由人工测试时,而不仅仅是集成),所以我认为这是一个控制器测试问题。正如我所发布的,我最好的客人是在控制器中,我从current_user 而不是params 拉用户。再一次,没有更聪明的......
    【解决方案2】:

    试试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 的屏幕记录 - 开始时有一些残酷的反馈循环,所以在开始的几秒钟内让你的电脑静音。

    【讨论】:

    • 有效!谢谢!为什么我需要assigns,尤其是。如果我那么做@user.upload?只是试图理解。再次感谢。
    • 很高兴它的工作!查看分配上的documentation - 它“为正在呈现的视图范围内的实例变量分配一个值。” - 如果这有帮助,请勾选我的答案。谢谢!
    • 刚才这个测试不是检查控制器中@user.first_name的值吗?这是否一定意味着put 按预期工作?如果put 成功,我认为测试应该按照最初指定的方式工作。
    • 测试将根据您的代码发送错误信号。如果更新失败,将呈现edit 模板,分配的用户名将是homer。测试通过了,但不是预期的结果。 update 失败。
    • @dax 非常感谢您花时间制作和发布视频。我明白你对这种做法的意思。在某种程度上,控制器测试部分是关于建立实例变量以供视图使用,我可以看到测试它们是如何有意义的。我还有一些问题,但我想考虑一下。具体来说,更新似乎涉及三个值:1)正在设置的新值,2)操作完成后数据库中的值,3)实例变量中的值。仅仅检查它们中的任何两个是否相等似乎是有问题的。
    【解决方案3】:

    回答

    感谢大家的建议,这些建议帮助我整理代码并找到问题所在。

    失败原因:默认出厂包含的参数包括电子邮件和密码,因此控制器测试不断尝试更改用户密码。

    具体来说,我在 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 正确地指出了我修复的问题。

    • 未清理参数(正在完成 rails4 升级)
    • 其他杂项。错误

    经验教训

    比较进出控制器的参数。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-29
      • 1970-01-01
      • 2015-09-15
      • 1970-01-01
      • 1970-01-01
      • 2014-06-07
      • 1970-01-01
      相关资源
      最近更新 更多