在 Rails4 中测试命名范围的最佳方法 [英] Best way to test named scopes in Rails4

查看:61
本文介绍了在 Rails4 中测试命名范围的最佳方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

作为从 Rails 3.2 迁移到 Rails 4 的一部分,所有命名范围都需要一个 proc 块.在此处阅读更多信息:http://edgeguides.rubyonrails.org/upgrading_ruby_on_rails.html#active-record

As a part of the migration from Rails 3.2 to Rails 4, all named scopes need a proc block. Read more here: http://edgeguides.rubyonrails.org/upgrading_ruby_on_rails.html#active-record

我错过了更新我的一个模型中的范围,最终在我迁移后在生产中咬了我.所以我想弄清楚如何测试这个问题,我发现了一些奇怪的行为.

I had missed updating a scope in one of my models, which ended up biting me in production after my migration. So I wanted to figure out how to test this issue, and I found some strange behavior.

在某些情况下,范围在没有 proc 的情况下似乎可以正常工作,但在其他情况下则不然.

In some cases the scopes appear to work fine without the proc, but not in other cases.

# models/offer.rb
class Offer < ActiveRecord::Base

  scope :roster, where(:on_roster => true)
  scope :commit, where("status_id > 5")

end

如果我在 rails 控制台中对独立调用使用每个作用域选项,则查询会正确构建,并且结果会按照 Rails 3.2 中的预期返回:

If I use each scope option on independent calls in rails console, the queries are built properly and the results come back as one would have expected in Rails 3.2:

$ rails c
2.0.0-p247 :001 > Offer.roster.all.size
  Offer Load (1.6ms)  SELECT "offers".* FROM "offers" WHERE "offers"."on_roster" = 't'
=> 1
2.0.0-p247 :002 > Offer.commit.all.size
  Offer Load (1.6ms)  SELECT "offers".* FROM "offers" WHERE (status_id > 5)
=> 3

但是,如果我在 rails 控制台中将两个作用域调用链接在一起,则每个查询中只包含链中最后一个作用域的约束:

However if I chain two scope calls together in the rails console, only the constraints from the last scope in the chain is included in each query:

2.0.0-p247 :003 > Offer.roster.commit.all.size
  Offer Load (1.4ms)  SELECT "offers".* FROM "offers" WHERE (status_id > 5)
 => 3
2.0.0-p247 :004 > Offer.commit.roster.all.size
  Offer Load (0.7ms)  SELECT "offers".* FROM "offers" WHERE "offers"."on_roster" = 't'
 => 1 

现在,如果我编辑我的模型以将 proc 添加到第二个命名范围,如下所示:

Now if I edit my model to add a proc to the second named scope, like so:

class Offer < ActiveRecord::Base

  scope :roster, where(:on_roster => true)
  scope :commit, -> { where("status_id > 5") }

end

如果定义了 proc 的命名范围位于链的末尾,它将使用两组约束构建查询.但是,如果未定义 proc 的命名范围位于链的末尾,则生成的查询将不受已定义 proc 的范围的约束.

If the named scope with the proc defined is at the end of the chain, it will build the query with both sets of constraints. However, if the named scope without the proc defined is at the end of the chain, the resulting query is built without the constraints of the scope with the proc defined.

$ rails c
2.0.0-p247 :003 > Offer.roster.commit.all.size
  Offer Load (1.4ms)  SELECT "offers".* FROM "offers" WHERE "offers"."on_roster" = 't' AND (status_id > 5)
 => 0 
2.0.0-p247 :004 > Offer.commit.roster.all.size
  Offer Load (0.7ms)  SELECT "offers".* FROM "offers" WHERE "offers"."on_roster" = 't'
 => 1 

所以第一个结果是正确的,并且加载了两个范围,但第二个结果不正确,只加载了最后一个范围.然后,如果您更改两个范围以使用 procs,如下所示:

So the first result is correct, and loads both scopes, but the second is incorrect and only loads the last scope. Then if you change both scopes to use procs, like so:

# models/offer.rb
class Offer < ActiveRecord::Base

  scope :roster, -> { where(:on_roster => true) }
  scope :commit, -> { where("status_id > 5") }

end

你终于得到了预期的行为:

You finally get the expected behavior:

$ rails c
2.0.0-p247 :002 > Offer.roster.commit.all.size
  Offer Load (1.3ms)  SELECT "offers".* FROM "offers" WHERE "offers"."on_roster" = 't' AND (status_id > 5)
 => 0
2.0.0-p247 :001 > Offer.commit.roster.all.size
  Offer Load (1.7ms)  SELECT "offers".* FROM "offers" WHERE "offers"."on_roster" = 't' AND (status_id > 5)
 => 0 

关于此的一个说明,在更新并保存模型后,在 Rails 控制台中调用 reload! 不会更新作用域的行为.您必须结束 Rails 控制台会话并开始新的会话,才能正确选择 proc 与非 proc.

One note on this, calling reload! in the rails console will not update the behavior of the scopes after you've updated and saved your model. You have to end your rails console session and begin a new one to get the proc vs. non-proc to pick up correctly.

我的问题是如何进行测试以确保我的所有示波器都按预期运行?每次我想测试它们是否有 proc 或 lambda 块时将作用域链接在一起似乎非常混乱.然而,我在示波器上设置的简单测试告诉我,我的所有示波器都通过了,并给出了假阳性结果.

The question I have is how to test to ensure that all of my scopes will behave as expected? Chaining scopes together each time I want to test whether they have a proc or lambda block seems very messy. However the simple tests I set up on the scopes, told me all my scopes were passing, and giving false positive results.

是否有一种简单的方法可以通过 Rspec 和 Rails4 来测试命名范围是否驻留在 proc 或 lambda 块中?

Is there an easy way to test via Rspec with Rails4 whether the named scope resides within a proc or lambda block?

推荐答案

作用域只是定义类方法的语法糖,因此通过查看代码很难知道您的作用域是否是 proc/lambda.

Scopes are only syntactic sugar for defining class methods, so by looking at the code it's quite impossible to know whether your scope was a proc/lambda or not.

我能想到的唯一解决方案是使用 RR 并代理作用域方法.这样你就可以在 body 没有响应调用时引发异常.在您的测试中,您比预期不会引发异常.但我怀疑这是否可行,因为一旦您设置了代理,类就已经被加载,因此作用域方法也已被调用.

Only solution I could think of is using RR and proxying the scope method. This way you could raise an exception if body does not respond to call. In your tests you than expect that no exception is raised. But I doubt this is going to work because once you set up the proxy the class has already been loaded and, thus, the scope method has been called.

我想与其通过测试强制使用 Proc,不如覆盖 scope 方法以根本不允许非 procs/-lambdas 是一个更好的主意.

I guess instead of enforcing Proc usage through test, overwriting the scope method to not allow non-procs/-lambdas at all is a better idea.

供参考:https://github.com/rails/rails/blob/master/activerecord/lib/active_record/scoping/named.rb

这篇关于在 Rails4 中测试命名范围的最佳方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆