Come testare un named_scope che fa riferimento a un attributo di classe con Shoulda?
-
19-08-2019 - |
Domanda
Ho le seguenti classi 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
e sto provando a testare il named_scope: current_user
utilizzando Dovrei ma quanto segue non funziona.
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
Il motivo per cui non funziona è perché la chiamata a User.current_user
nel metodo should_have_named_scope
viene valutata quando la classe viene definita e io cambio il valore di current_user
successivamente nel blocco setup
durante l'esecuzione del test.
Ecco cosa mi è venuto in mente per testare questo 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
Quindi come lo testeresti usando Shoulda ?
Soluzione
Penso che tu stia procedendo nel modo sbagliato. Innanzitutto, perché è necessario utilizzare un ambito denominato? Non lo fai e basta?
class BatchRecord < ActiveRecord::Base
belongs_to :user
def current_user
self.user.class.current_user
end
end
Nel qual caso sarebbe banale testarlo. MA! WTF stai definendo current_user
come attributo di classe? Ora che Rails 2.2 è "sicuro per i thread" cosa accadrebbe se eseguissi la tua app in due thread separati? Un utente effettuerebbe l'accesso, impostando current_user
per TUTTE le istanze User
. Ora un altro utente con privilegi di amministratore accede e current_user
passa alla loro istanza. Quando il primo utente passa alla pagina successiva, avrà accesso all'account delle altre persone con i loro privilegi di amministratore! Shock! Orrore!
Ciò che raccomando di fare in questo caso è di creare un nuovo metodo controller current_user
che restituisca l'istanza dell'utente corrente dell'utente. Puoi anche fare un ulteriore passo avanti e creare un modello wrapper come:
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
Oh, a proposito, ora guardo di nuovo la tua domanda, forse uno dei motivi per cui non funziona è che la riga EDIT Sono un idiota. User.current_user & amp; & amp; User.current_user.id
restituirà un valore booleano, anziché l'Integer che desideri.
L'ambito denominato è davvero il modo assolutamente sbagliato di farlo. L'ambito denominato ha lo scopo di restituire raccolte, piuttosto che singoli record (che è un altro motivo per cui ciò fallisce). Sta inoltre effettuando una chiamata non necessaria al DB risultante in una query non necessaria.
Altri suggerimenti
Ho appena realizzato che la risposta mi sta fissando. Dovrei lavorare dall'altra parte dell'associazione che sarebbe current_user.batch_records
. Quindi ho semplicemente testato il named_scope
sul modello User
e tutto è a posto.
@ Chris Lloyd - Per quanto riguarda il problema di sicurezza del thread, l'attributo current_user
è impostato da un before_filter
nel mio ApplicationController
, quindi viene modificato per richiesta. Capisco che esiste ancora il potenziale di disastro se ho scelto di correre in un ambiente multi-thread (che attualmente non è il caso). Suppongo che quella soluzione sarebbe interamente un altro argomento.