【问题标题】:Rspec expect receive(...).with(time_range)Rspec期望接收(...).with(time_range)
【发布时间】:2017-07-28 08:30:12
【问题描述】:

根据rspec expect message docs,当我们使用如下代码时:

expect(double).to receive(:message).with(argument)

并且argument 响应#===,Rspec 将对实际传入的任何内容调用argument.===。但是如果我想检查一个时间范围是否实际传入,我该怎么办?

例如,我想测试此方法是否使用正确的参数发送正确的消息(已简化):

def in_date_range(range)
  activities.by_created_at(range)
end

还有一个类似的测试:

it 'passes correct arguments' do
  range = Time.new(0)..(Time.new(0) + 1.hour)

  expect(activities).to receive(:by_created_at).with(range)

  in_date_range(range)
end

TypeError: can't iterate from Time 失败

我怀疑这是因为哪个 rspec 试图在我的时间范围内调用 #=== 并遇到麻烦。

有没有办法检查我的时间范围实际上是我传入的范围?

我实际上并不是在寻找重叠的时间,而是寻找相同的范围:具有相同的起点和终点。

我想我们可以做到这一点的一种方法是编写一个自定义匹配器,但这似乎是一条不寻常的路线。

编辑

另一种可能的解决方案是将方法分成两部分:

def compute_range(time_in)
  (computations)
  time1..time2
end

def in_date_range(time_in)
  range = compute_range(time_in)
  activities.by_created_at(range)
end

然后我可以测试它返回正确范围的计算方法,对于我可以测试的较大方法,它传入正确的输入,并获取较小方法的输出,它从第一个获取结果并传递给另一种方法,避免了消息中检查范围的混淆。

这并没有完全回答这个问题 - 你如何使用 rspec 接收(:消息)检查“接收特定范围”,但现在对我来说已经足够了。

【问题讨论】:

    标签: ruby-on-rails ruby rspec


    【解决方案1】:

    当然,您可以只使用expect(..).to receive(:foo) { |arg| do_something } 并在 do_something 块中检查您的期望。以下应该有效:

    range = Time.new(0)..(Time.new(0) + 1.hour)
    
    expect(activities).to receive(:by_created_at) { |arg| 
                            expect(arg.first).to eq(Time.new(0))
                          }
    

    这是可行的,因为一个失败的期望会引发一个 rspec 特定的异常,并且 RSpec 运行程序并不关心在执行时会在哪里引发这样的异常,只要它是在执行 it 块期间。

    【讨论】:

      【解决方案2】:

      来自makandracards

      在 Ruby 或 Rails 中测试两个日期范围是否重叠

      A 检查两个日期或时间范围 A 和 B 是否重叠需要涵盖很多情况:

      A 部分重叠 B A包围B B包围A A 完全出现在 B 之后 B 完全发生在 A 之后 使用单个表达式涵盖所有这些情况的一个技巧是查看每个范围的开始日期是否以相同方向查看另一个范围的结束日期。

      下面的代码展示了如何在 Ruby on Rails 中实现这一点。该示例是一个Interval 类,它有两个属性#start_date 和#end_date。这些日期被认为是包容性的,这意味着如果一个间隔在另一个间隔开始的那一天结束,我们认为这两个间隔重叠。

      请注意我们是如何实现加载 Ruby 对象的版本(Interval#overlaps?)和返回所有重叠间隔的范围(Interval.overlapping)的。根据您的问题,您可能需要其中一个或两个。

      class Interval < ActiveRecord::Base
      
        validates_presence_of :start_date, :end_date
      
        # Check if a given interval overlaps this interval    
        def overlaps?(other)
          (start_date - other.end_date) * (other.start_date - end_date) >= 0
        end
      
        # Return a scope for all interval overlapping the given interval, including the given interval itself
        named_scope :overlapping, lambda { |interval| {
          :conditions => ["id <> ? AND (TIMEDIFF(start_date, ?) * TIMEDIFF(?, end_date)) >= 0", interval.id, interval.end_date, interval.start_date]
        }}
      
      end
      

      算法说明

      您可以将算法想象成这样:“如果两个范围的起点都在另一个范围的终点,那么它们是否都朝同一个方向看?”

      如果你在一张纸上作画,你会发现这句话对于两个范围重叠的所有情况都是正确的:

      A 部分重叠 B A包围B B包围A 您还可以看到,在范围不重叠的情况下,该语句是错误的:

      A 完全出现在 B 之后 B 完全发生在 A 之后

      【讨论】:

      • 啊,很遗憾我的问题还不够清楚。我不是在寻找一个时间在某个时间范围内的测试,而是一个范围等于另一个范围的测试。
      • 如果你看,它就是这样做的。它比较两组开始和两组结束。
      • 当我说“相等”时,我的意思是“开始时间相同,结束时间相同”。您的函数会检查重叠,除非我完全误解了它。顺便说一句,我很欣赏优雅的数学。这是您的问题的一个干净的解决方案,不幸的是这不是我的问题。
      【解决方案3】:

      编辑:

      我有更多的时间来看看这个,我认为错误在 activities.by_created_at(range) 的某个地方。这一定是试图在范围内调用循环。

      以下是通过规范。我创建了一个虚拟的 Thing 类并针对它运行测试,一切都通过了:

      class Thing
        def initialize( name )
          @name = name
        end
      
        def in_date_range(range)
          activities.by_created_at(range)
        end
      end
      
      describe Thing do
        specify 'Thing test' do
          name = 'Tim'
          expect(Thing).to receive(:new).with( name )
      
          Thing.new(name)
        end
      
        specify 'Time test' do
          range = Time.new(0)..(Time.new(0) + 1.hour)
          t = Thing.new('Jimmy')
      
          expect(t).to receive(:in_date_range).with( range )
      
          t.in_date_range( range )
        end
      end
      
      ~/test (master)
      18:22:21$ rspec spec/models/thing_spec.rb 
      Loading Enhanced Logger.
      Run options: include {:focus=>true}
      ..
      
      Finished in 0.14094 seconds (files took 2.89 seconds to load)
      2 examples, 0 failures
      

      结束编辑

      问题不在于 rspec 检查范围。

      我运行了这个,它成功了:

      describe Thing do
        specify 'Thing test', :focus do
          range = 1..4
          expect(Thing).to receive(:new).with( range )
      
          Thing.new(1..4)
        end
      end
      
      
      Run options: include {:focus=>true}
      .
      
      Finished in 0.09132 seconds (files took 2.59 seconds to load)
      1 example, 0 failures
      

      问题在于使 Time 可迭代。

      看到这个问题:Iterate over Ruby Time object with delta

      已接受答案的第二部分应该对您有所帮助。

      【讨论】:

      • 哦,有趣!我不知道为什么我没有检查。这说明了一些问题。编辑问题以指定时间实例,但我不明白使时间可迭代的相关性
      猜你喜欢
      • 2016-05-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多