【问题标题】:Rails / Rspec - writing spec for delegate method (allow_nil option)Rails / Rspec - 为委托方法编写规范(allow_nil 选项)
【发布时间】:2012-02-03 03:07:53
【问题描述】:

给定以下代码:

(1) 您将如何编写规范来测试 :allow_nil => false 选项?

(2) 是否值得编写规范进行测试?

class Event < ActiveRecord::Base
  belongs_to :league

  delegate :name, :to => :league, :prefix => true, :allow_nil => false
end

describe Event do

  context 'when delegating methods to league object' do
    it { should respond_to(:league_name) }
  end

end

如果你可以扩展 shoulda 来做,那就太好了:

it { should delegate(:name).to(:league).with_options(:prefix => true, :allow_nil => false) }

【问题讨论】:

标签: delegation rspec-rails


【解决方案1】:

根据delegate rails 模块的文档:

如果委托对象为 nil,则会引发异常,无论 nil 是否响应委托方法,都会发生这种情况。您可以使用 :allow_nil 选项获得 nil。

我会创建一个带有 nil league 的 Event 对象 event,或者设置 event.league = nil,然后尝试调用 event.name,并检查它是否引发了异常,因为这是应该发生的情况allow_nil 为 false(这也是默认值)。我知道 rspec 有这个用于异常测试的习惯用法:

lambda{dangerous_operation}.should raise_exception(optional_exception_class)

我不确定是否应该有这个构造,虽然有are some articles, kinda old, about how to get this behavior in shoulda。

我认为如果该类的用户可以预期或假设会发生这种行为,我认为这是值得测试的——我认为在这种情况下可能是正确的。我不会扩展 shoulda 来测试“应该委托”,因为这似乎更依赖于实现:你真的是说你的 Event 如果你尝试调用 #name 当它有一个 nil 联盟时应该引发一个异常。对于 Event 的用户来说,你如何做到这一点真的不重要。如果你想断言并记下这种行为,我什至会去测试Event#name 与League#name 具有相同的语义,没有提及任何关于delegate 的内容,因为这是一种以行为为中心的方法。

根据代码的行为方式而不是代码的构建方式构建测试 - 以这种方式测试对于那些偶然进入您的测试的人来说是更好的文档,因为他们对“为什么我的事件抛出?”这个问题更感兴趣。或“什么会导致 Event 抛出?”而不是“这个事件是委托吗?”。

您可以通过想象如果您以 Event 用户不应该关心的方式更改代码可能会发生什么故障来突出这种情况。如果他们不关心它,那么当您更改它时测试不应该中断。例如,如果您想自己处理委托,方法是编写一个#name 函数,该函数首先记录或递增一个计数器,然后然后 委托给league?通过测试引发异常的行为,您可以免受此更改的影响,但是通过测试 Event 是否是委托,您将在进行此更改时中断该测试 - 因此您的测试并没有查看真正的内容 对#name 的调用很重要。

无论如何,这只是肥皂盒的谈话。 tl; dr:测试它是否是某人可能依赖的行为。任何未经测试的东西都是 Shroedinger 的猫:同时破碎和不破碎。说实话,很多时候这都是可以的:您是想对系统应该如何表现提出严格而明确的看法,还是只是让它成为“未指定的行为”,这取决于您的喜好。

【讨论】:

  • 您必须测试异常是否为 RuntimeError 否则,简单地不委托将引发未找到的方法。
【解决方案2】:

所以这里有几点关于是否要测试这个:

1) 我不认为规范这种行为有什么问题。如果您的用户需要学习您的软件/库,那么确保作为面向公众的合同一部分的所有方法都经过规范通常非常有帮助。如果你不想让这部分成为这个模型的 api,我可能会建议手动进行委托,以免向外界暴露比你需要的更多的方法。

2) 此类规范有助于确保您与委托对象之间的合同仍然有效。如果您在测试中使用存根和模拟,这将特别有用,因为它们通常实现相同的合约,因此至少您知道该合约何时更改。

就你如何测试它的 allow_nil 部分而言,我同意马特的观点,最好的办法是确保联赛为零,然后尝试在联赛中调用名称。您可以对此进行测试以确保返回 nil。

希望这会有所帮助。

【讨论】:

    【解决方案3】:

    我会测试委托,因为我们有效地创建了两个类之间的契约

    describe '#league_name' do
      let(:event) { create(:event) }
    
      after { event.league_name }
    
      it "delegates to league's name with prefix" do
         expect(event.league).to receive(:name)
      end
    end
    

    【讨论】:

      【解决方案4】:

      现在应该检查 allow nil 选项。

      class Account
        delegate :name, to: :league, allow_nil: true
      end
      
      # RSpec
      describe Account do
        it { should delegate_method(:name).to(:league).allow_nil }
      end
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-05-31
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多