How to test a scope in Rails 3

前端 未结 6 503
夕颜
夕颜 2020-12-24 14:16

What\'s the best way to test scopes in Rails 3. In rails 2, I would do something like:

Rspec:

it \'should have a top_level scope\' do
  Category.top         


        
相关标签:
6条回答
  • 2020-12-24 14:36

    FWIW, I agree with your original method (Rails 2). Creating models just for testing them makes your tests way too slow to run in continuous testing, so another approach is needed. Loving Rails 3, but definitely missing the convenience of proxy_options!

    0 讨论(0)
  • 2020-12-24 14:39

    Quickly Check the Clauses of a Scope

    I agree with others here that testing the actual results you get back and ensuring they are what you expect is by far the best way to go, but a simple check to ensure that a scope is adding the correct clause can also be useful for faster tests that don't hit the database.

    You can use the where_values_hash to test where conditions. Here's an example using Rspec:

    it 'should have a top_level scope' do
      Category.top_level.where_values_hash.should eq {"parent_id" => nil}
    end
    

    Although the documentation is very slim and sometimes non-existent, there are similar methods for other condition-types, such as:

    order_values

    Category.order(:id).order_values
    # => [:id]
    

    select_values

    Category.select(:id).select_values
    # => [:id]
    

    group_values

    Category.group(:id).group_values
    # => [:id]
    

    having_values

    Category.having(:id).having_values
    # => [:id]
    

    etc.

    Default Scope

    For default scopes, you have to handle them a little differently. Check this answer out for a better explanation.

    0 讨论(0)
  • 2020-12-24 14:43

    Ideally your unit tests should treat models (classes) and instances thereof as black boxes. After all, it's not really the implementation you care about but the behavior of the interface.

    So instead of testing that the scope is implemented in a particular way (i.e. with a particular set of conditions), try testing that it behaves correctly—that it returns instances it should and doesn't return instances it shouldn't.

    describe Category do
      describe ".top_level" do
        it "should return root categories" do
          frameworks = Category.create(:name => "Frameworks")
    
          Category.top_level.should include(frameworks)
        end
    
        it "should not return child categories" do
          frameworks = Category.create(:name => "Frameworks")
          rails = Category.create(:name => "Ruby on Rails", :parent => frameworks)
    
          Category.top_level.should_not include(rails)
        end
      end
    end
    

    If you write your tests in this way, you'll be free to re-factor your implementations as you please without needing to modify your tests or, more importantly, without needing to worry about unknowingly breaking your application.

    0 讨论(0)
  • 2020-12-24 14:51

    This is how i check them. Think of this scope :

      scope :item_type, lambda { |item_type|
        where("game_items.item_type = ?", item_type )
      } 
    

    that gets all the game_items where item_type equals to a value(like 'Weapon') :

        it "should get a list of all possible game weapons if called like GameItem.item_type('Weapon'), with no arguments" do
            Factory(:game_item, :item_type => 'Weapon')
            Factory(:game_item, :item_type => 'Gloves')
            weapons = GameItem.item_type('Weapon')
            weapons.each { |weapon| weapon.item_type.should == 'Weapon' }
        end
    

    I test that the weapons array holds only Weapon item_types and not something else like Gloves that are specified in the spec.

    0 讨论(0)
  • 2020-12-24 14:56

    Leaving the question of 'how-to-test' aside... here's how to achieve similar stuff in Rails3...

    In Rails3 named scopes are different in that they just generate Arel relational operators. But, investigate!

    If you go to your console and type:

    # All the guts of arel!
    Category.top_level.arel.inspect
    

    You'll see internal parts of Arel. It's used to build up the relation, but can also be introspected for current state. You'll notice public methods like #where_clauses and such.

    However, the scope itself has a lot of helpful introspection public methods that make it easier than directly accessing @arel:

    # Basic stuff:
    => [:table, :primary_key, :to_sql]
    
    # and these to check-out all parts of your relation:
    => [:includes_values, :eager_load_values, :preload_values,
        :select_values, :group_values, :order_values, :reorder_flag,
        :joins_values, :where_values, :having_values, :limit_value,
        :offset_value, :readonly_value, :create_with_value, :from_value]
    
    # With 'where_values' you can see the whole tree of conditions:
    Category.top_level.where_values.first.methods - Object.new.methods
    => [:operator, :operand1, :operand2, :left, :left=, 
        :right, :right=, :not, :or, :and, :to_sql, :each]
    
    # You can see each condition to_sql
    Category.top_level.where_values.map(&:to_sql)
    => ["`categories`.`parent_id` IS NULL"]
    
    # More to the point, use #where_values_hash to see rails2-like :conditions hash:
    Category.top_level.where_values_hash
    => {"parent_id"=>nil}
    

    Use this last one: #where_values_hash to test scopes in a similar way to #proxy_options in Rails2....

    0 讨论(0)
  • 2020-12-24 14:58

    Don't know if this helps or not, but I'm looking for a solution and ran across this question.

    I just did this and it works for me

    it { User.nickname('hello').should == User.where(:nickname => 'hello') }
    
    0 讨论(0)
提交回复
热议问题