如何在Rails 3中测试范围

什么是在Rails 3中测试作用域的最佳方法。在rails 2中,我会做类似的事情:

Rspec的:

it 'should have a top_level scope' do Category.top_level.proxy_options.should == {:conditions => {:parent_id => nil}} end 

这在rails 3中失败,并为[]:ActiveRecord :: Relation“错误提供了”未定义的方法`proxy_options’。

人们如何测试使用正确的选项指定范围? 我看到你可以检查一下arel对象,也许可以对此做出一些期望,但我不确定最好的方法是什么。

抛开“如何测试”的问题…这里是如何在Rails3中实现类似的东西……

在Rails3中,命名范围的不同之处在于它们只生成Arel关系运算符。 但是,调查!

如果你去你的控制台并键入:

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

你会看到Arel的内部部分。 它用于建立关系,但也可以对当前状态进行反省。 您会注意到#where_clauses等公共方法。

但是,范围本身有很多有用的内省公共方法,比直接访问@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} 

使用最后一个:#where_values_hash以与Rails2中的#proxy_options类似的方式测试范围….

理想情况下,您的unit testing应将模型(类)及其实例视为黑盒子。 毕竟,它不是你关心的实现,而是界面的行为。

因此,不是测试范围是以特定方式实现的(即使用一组特定条件),而是尝试测试它的行为是否正确 – 它返回应该应用的实例,而不返回它不应该返回的实例。

 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 

如果您以这种方式编写测试,您可以随意重新考虑实现,而无需修改测试,更重要的是,无需担心在不知不觉中破坏您的应用程序。

这就是我检查它们的方式。 想想这个范围:

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

获得所有game_items,其中item_type等于一个值(如’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 

我测试武器arrays只包含武器item_types而不是像规范中指定的手套那样的东西。

不知道这是否有帮助,但我正在寻找解决方案,并遇到了这个问题。

我这样做了,它对我有用

它{User.nickname('hello')。should == User.where(:nickname =>'hello')}

FWIW,我同意你原来的方法(Rails 2)。 创建仅用于测试它们的模型会使您的测试速度太慢而无法在连续测试中运行,因此需要另一种方法。 Loving Rails 3,但肯定错过了proxy_options的便利!

快速检查范围的条款

我同意这里的其他人一样,测试你得到的实际结果并确保它们是你期望的是迄今为止最好的方法,但是确保范围正在添加正确的子句的简单检查对于更快的测试也是有用的没有命中数据库。

您可以使用where_values_hash来测试条件的位置。 这是使用Rspec的示例:

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

尽管文档非常简陋且有时不存在,但其他条件类型也有类似的方法,例如:

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] 

等等

默认范围

对于默认范围,您必须以不同的方式处理它们。 请查看此答案以获得更好的解释。