Shouldaでクラス属性を参照するnamed_scopeをテストする方法は?
-
19-08-2019 - |
質問
次のActiveRecordクラスがあります:
class User < ActiveRecord::Base
cattr_accessor :current_user
has_many :batch_records
end
class BatchRecord < ActiveRecord::Base
belongs_to :user
named_scope :current_user, lambda {
{ :conditions => { :user_id => User.current_user && User.current_user.id } }
}
end
Shoulda を使用してnamed_scope :current_user
をテストしようとしていますが、以下は機能しません。
class BatchRecordTest < ActiveSupport::TestCase
setup do
User.current_user = Factory(:user)
end
should_have_named_scope :current_user,
:conditions => { :assigned_to_id => User.current_user }
end
機能しない理由は、クラスが定義されているときにUser.current_user
メソッドのshould_have_named_scope
の呼び出しが評価され、その後current_user
でsetup
の値を変更するためですテストの実行中にブロックします。
このnamed_scopeをテストするために思いついたのは次のとおりです。
class BatchRecordTest < ActiveSupport::TestCase
context "with User.current_user set" do
setup do
mock_user = flexmock('user', :id => 1)
flexmock(User).should_receive(:current_user).and_return(mock_user)
end
should_have_named_scope :current_user,
:conditions => { :assigned_to_id => 1 }
end
end
では、 Shoulda を使用して、これをどのようにテストしますか?
解決
あなたはこれについて間違った方法で行っていると思います。まず、名前付きスコープを使用する必要があるのはなぜですか?これだけでいいですか?
class BatchRecord < ActiveRecord::Base
belongs_to :user
def current_user
self.user.class.current_user
end
end
その場合、テストするのは簡単です。しかし! WTFはcurrent_user
をクラス属性として定義していますか? Rails 2.2は<!> quot; threadsafe <!> quot;アプリを2つの別々のスレッドで実行している場合はどうなりますか? 1人のユーザーがログインし、すべてのUser
インスタンスにUser.current_user && User.current_user.id
を設定します。これで、管理者権限を持つ別のユーザーがログインし、<=>がインスタンスに切り替わります。最初のユーザーが次のページに移動すると、管理者権限を持つ他のユーザーアカウントにアクセスできます。衝撃!ホラー!
この場合の推奨事項は、現在のユーザーのユーザーインスタンスを返す新しいコントローラーメソッド<=>を作成することです。さらに一歩進んで、次のようなラッパーモデルを作成することもできます。
class CurrentUser
attr_reader :user, :session
def initialize(user, session)
@user, @session = user, session
end
def authenticated?
...
end
def method_missing(*args)
user.send(*args) if authenticated?
end
end
ああ、ところで、私はあなたの質問をもう一度見てみましょう。 編集私はバカです。
名前付きスコープは、実際にこれを行うための絶対に間違った方法です。名前付きスコープは、個々のレコードではなくコレクションを返すことを目的としています(これが失敗する別の理由です)。また、DBに不要な呼び出しを行って、不要なクエリを作成しています。
他のヒント
I just realized the answer is staring right at me. I should be working from the other side of the association which would be current_user.batch_records
. Then I simply test the named_scope
on the User
model and everything is fine.
@Chris Lloyd - Regarding the thread safety issue, the current_user
attribute is being set by a before_filter
in my ApplicationController
, so it is modified per request. I understand that there is still the potential for disaster if I chose to run in a multi-threaded environment (which is currently not the case). That solution I suppose would be another topic entirely.