Rspec期望收到(...)。with(time_range)

时间:2017-03-07 23:06:11

标签: ruby-on-rails ruby rspec

根据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 receive(:message)检查'接收特定范围',但现在对我来说已经足够了。

3 个答案:

答案 0 :(得分: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块的过程中会在何处引发此类异常。

答案 1 :(得分:0)

修改

我有更多时间来看这个,我认为错误位于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

问题在于使时间可迭代。

请参阅此问题:Iterate over Ruby Time object with delta

接受答案的第二部分可以帮助你。

答案 2 :(得分:0)

来自makandracards

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

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

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

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

注意我们如何实现加载的Ruby对象的版本(Interval#overlapps?)和返回所有重叠间隔的范围(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

算法说明

您可以像这样对算法进行描绘:“如果两个范围的开头都看到另一个范围的结尾,那么它们是否都朝同一方向看?”

如果你在一张纸上画画,你可以看到这个陈述适用于两个范围重叠的所有情况:

部分重叠B. A环绕B. B围绕A 在范围不重叠的情况下,您还可以看到该语句为false:

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

之后