如何在Rails 3中测试作用域

Tre*_*ean 24 ruby-on-rails ruby-on-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
Run Code Online (Sandbox Code Playgroud)

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

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

adz*_*ies 37

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

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

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

# All the guts of arel!
Category.top_level.arel.inspect
Run Code Online (Sandbox Code Playgroud)

你会看到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}
Run Code Online (Sandbox Code Playgroud)

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

  • 那么......如何测试rails3范围?一个例子可能吗? (7认同)
  • `where_values_hash`对我来说似乎不起作用.`where_values_hash`给了我一个空哈希,但`where_values`返回一个带有正确SQL的数组. (4认同)

Ian*_*nce 27

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

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

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
Run Code Online (Sandbox Code Playgroud)

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

  • 这根本不是测试Rails.您使用命名范围实现它的事实并不重要.地狱,它可能是一个普通的类方法,它调用`connection.select`并手工构建对象.无论你是否使用Rails助手,都不会使你的*方法变得更少.所以,请继续进行测试. (7认同)
  • 我认为这是因为我们无法确定我们编写的范围是否正确.如果它涉及许多条件,则尤其如此. (2认同)
  • @fivetwentysix因为我们关心的是范围返回正确的记录.我们不关心它是如何实现的(甚至不管它是否是Rails范围!),所以我们需要点击DB来测试它. (2认同)

Spy*_*ros 5

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

  scope :item_type, lambda { |item_type|
    where("game_items.item_type = ?", item_type )
  } 
Run Code Online (Sandbox Code Playgroud)

获得所有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
Run Code Online (Sandbox Code Playgroud)

我测试武器阵列只包含武器item_types而不是规格中指定的其他类似手套的东西.

  • .each方法的问题在于可能会出现误报:如果查询出现问题且返回零武器,则测试将通过.为了解决这个问题,您可以添加一个额外的测试来检查返回的> 0武器. (4认同)