2009-01-21 6 views
0

J'ai les classes ActiveRecord suivantes:Comment tester un named_scope qui fait référence à un attribut de classe avec Shoulda?

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 

et je suis en train de tester le named_scope :current_user en utilisant Shoulda mais ce qui suit ne fonctionne pas.

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 

La raison pour laquelle il ne fonctionne pas est parce que l'appel à User.current_user dans la méthode should_have_named_scope est en cours d'évaluation lorsque la classe est définie et je suis changer la valeur de current_user ensuite dans le bloc setup lors de l'exécution du tester.

Voici ce que je suis venu avec pour tester cette 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 

Alors, comment voulez-vous tester cela en utilisant Shoulda?

Répondre

1

Je pense que vous allez à ce sujet dans le mauvais sens. Premièrement, pourquoi avez-vous besoin d'utiliser une portée nommée? Ne faites-vous pas cela?

class BatchRecord < ActiveRecord::Base 
    belongs_to :user 

    def current_user 
    self.user.class.current_user 
    end 
end 

Dans ce cas, il serait trivial à tester. MAIS! WTF définissez-vous current_user en tant qu'attribut de classe? Maintenant que Rails 2.2 est "threadsafe", que se passerait-il si vous utilisiez votre application en deux threads séparés? Un utilisateur se connecterait, en définissant le current_user pour toutes les instances User. Maintenant, un autre utilisateur avec des privilèges d'administrateur se connecte et current_user est basculé vers leur instance. Lorsque le premier utilisateur va à la page suivante, il/elle aura accès au compte des autres personnes avec leurs privilèges d'administrateur! Choc! Horreur!

Ce que je vous recommande de faire dans ce cas est de créer une nouvelle méthode de contrôleur current_user qui renvoie l'instance User de l'utilisateur actuel. Vous pouvez aussi aller un peu plus loin et de créer un modèle d'emballage comme:

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, et en passant, maintenant je regarde votre question peut-être l'une des raisons pour lesquelles il ne fonctionne pas est que la ligne User.current_user && User.current_user.id retournera un booléen, plutôt que l'entier que vous voulez. EDIT Je suis un idiot.

La portée nommée est vraiment la façon absolument incorrecte de le faire. La portée nommée est destinée à renvoyer des collections, plutôt que des enregistrements individuels (ce qui est une autre raison pour laquelle cela échoue). Il fait également un appel inutile à la base de données résultant en une requête dont vous n'avez pas besoin.

+0

Les opérateurs logiques Ruby (&&, ||) ne fonctionnent pas comme vous le suggérez. Ils renvoient le dernier argument évalué.Ainsi, le code current_user ne retournera un booléen que si User.current_user ou User.current_user.id est un booléen - ce que je suppose que ce n'est pas le cas. – Chuck

+0

Oups, vous avez raison! Boire tôt le matin n'aidait pas avec mes compétences Ruby. –

0

Je viens de réaliser que la réponse me regarde droit dans les yeux. Je devrais travailler de l'autre côté de l'association qui serait current_user.batch_records. Ensuite, je simplement tester le named_scope sur le modèle User et tout va bien.

@Chris Lloyd - En ce qui concerne la question de la sécurité des threads, l'attribut current_user est en cours d'un before_filter dans mon ApplicationController, il est modifié par demande. Je comprends qu'il existe toujours un risque de catastrophe si je choisis de fonctionner dans un environnement multithread (ce qui n'est pas le cas actuellement). Cette solution, je suppose, serait un autre sujet entièrement.

+0

http://m.onkey.org/2008/10/23/thread-safety-for-your-rails#comment-2601 –

+0

Avant, je devais constamment passer current_user, je suppose qu'après avoir lu ça, je vais juste devoir vivre avec. Merci pour vos commentaires. –

Questions connexes