0

特徴

ユーザーにはプロファイルがあり、それを更新できる必要があります。


問題

プロファイルを更新します。たとえば、名前を「ホーマー シンプソン」に変更しますが、データベース レコードが更新されていないように見えるため、すべてのアサーションが失敗します。

更新された属性を取得できないようです:

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

NB私は両方を試しまし@user.reloadsubject.current_user.reload

仕様はまだ合格していません。


コード

私は使っている:

  • レール (4.0.0)
  • 考案 (3.0.3)
  • rspec レール (2.14.0)
  • カピバラ (2.1.0)
  • factory_girl (4.2.0)
  • データベースクリーナー (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のように交換しようとしました: 「Eメールアドレスを確認しようとしているかのように更新時にRspec登録コントローラーテストが失敗する」subject.current_user

  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.idvia ではなくby でユーザーを見つけます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.rblogin_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

私の統合仕様は問題なく通過します。ここでコントローラーに何が欠けていますか?

4

3 に答える 3

2

私の答えはあなたの問題を解決できますが、コードのバグを直接修正することはできません。そのためには、より多くのテストと実践的なデバッグを書く必要があります。私は、読むだけでそれを理解する経験があまりありません:)

問題のようなDeviseのRegistrationsControllerをオーバーライドすることはお勧めしません。元のコードと比較すると、あなたのコードには少なくとも次の 2 つの点が欠けています。

  1. current_user オブジェクトのコピーはありません。実際のアプリでは、フォームを送信することで current_user がログアウトされますが、これは適切ではありません。

  2. パラメータのサニタイズの欠如

そして残りのバグ。

私の提案は、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 には完全にカバーされた機能テストがあるため、繰り返す必要はありません。

于 2013-09-25T15:48:08.110 に答える
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

update : 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 チュートリアルに基づいています。彼はこの方法を使用しており、その理由を説明していると思いますが、現時点ではスクリーン キャストはありません。後で調べます。

ビデオ:

これがビデオです - 私はquicktimeのスクリーンレコードを使用しただけなので、非常に低品質です - そして最初にいくつかの残忍なフィードバックループがあるので、最初の数秒間はコンピュータをミュートしてください.

于 2013-09-25T14:38:08.050 に答える
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 は私が修正した問題を指摘してくれました。

  • パラメータはサニタイズされませんでした (Rails4 のアップグレードを終了中です)
  • その他雑多。バグ

学んだ教訓

コントローラーに出入りするパラメーターを比較します。

電子メールまたはパスワードを変更しようとしていないため、統合テストに合格していました。

于 2013-09-26T09:23:13.800 に答える