如何将ruby方法不再响应方法而是调用方法?

时间:2017-03-08 22:03:47

标签: ruby-on-rails ruby rspec rspec-rails

我正在使用一个小方法来检查租户是否启用了功能。每个租户都有一个存放旗帜的租户计划。我还在Tenant模型上有一些标志,可以用来覆盖特定于计划的标志。在租户模型中,一个小特征检测方法如下所示:

def has_feature?(feature, feature_default = false)
  if tenant_plan
    feature = "#{feature}_enabled?".to_sym
    return tenant_plan.send(feature) || (respond_to?(feature) and send(feature))
  end
  feature_default
end

我对这个问题最感兴趣的部分是:

(respond_to?(feature) and send(feature))

我正在检查Tenant模型上是否存在同名字段,如果有,则会得到结果。

如果我使用控制台来检查这个:

[1] pry(main)> false and tenant.send(:free_courses_enabled?)
=> false

[2] pry(main)> true and tenant.send(:free_courses_enabled?)
NoMethodError: undefined method `free_courses_enabled?' for #<Tenant:0x007f8f7171d6c8>
from /Users/typeoneerror/.rvm/gems/ruby-2.2.4@doki/gems/activemodel-4.2.7.1/lib/active_model/attribute_methods.rb:433:in `method_missing'

如果Tenant没有send功能方法,我希望respond_to?甚至不被调用,这在控制台测试中看起来就像是这样,但我在测试中发现即使respond_to?返回false,也会调用send,但不会引发NoMethodError

expect(tenant).not_to receive(:free_courses_enabled?)

结果:

expected no Exception, got #<RSpec::Mocks::MockExpectationError: (#<Tenant:0x007fdfd908a2a8>).free_courses_enabled?(no args)
           expected: 0 times with any arguments
           received: 1 time> with backtrace:

有点滑稽,当我期待相反时:

expect(tenant).to receive(:free_courses_enabled?).and_call_original

我们得到了

 NoMethodError:
   undefined method `free_courses_enabled?' for #<Tenant:0x007f8254bfedd8>

我很茫然。

在这种情况下,ruby是否会在调用方法时吞下NoMethodError异常,因为表达式的第一部分返回false?非常感谢您的解释,并乐于听取有关重写的任何建议。

它让我看起来像是一个带有ActiveModel的RSpec错误。当我没有添加期望时,当我在开发中运行代码时,它会按预期运行。

1 个答案:

答案 0 :(得分:0)

我想在把我的脑子投入各种图书馆后,我想到了这一点。 Tenant未定义名为payment_options_features?的方法,因此当我进入控制台并执行此操作时:

tenant.respond_to?(:payment_options_features?) # false

但是,我相信我希望租户不应该在我的RSpec测试中接收这个方法......

expect(tenant).not_to receive(:payment_options_features?)

实际上在租户对象上创建了该函数的模拟,所以当测试运行时:

tenant.respond_to?(:payment_options_features?) # true

因为它被“嘲笑”了。另一种方法是这样做,因为我们知道send是一种应该始终存在的方法。

expect(tenant).not_to receive(:send).with('payment_options_features?')