Stubbing Scopes

Hi!

I’m trying to test the following (simplified) model:

class Allocation < ActiveRecord::Base
scope :in_interval, (proc do |start_of_interval, end_of_interval|
params = {:s => start_of_interval, :e => end_of_interval}
where("(starts_at > :s AND starts_at < :e) OR (ends_at > :s AND
ends_at < :e) OR (starts_at <= :s AND ends_at >= :e)", params)
end)

scope :on_day, (proc do |day|
day = day.to_time.beginning_of_day
in_interval(day, day + 1.day)
end)

validations and other scopes…

end

I wrote a lot of specs for :in_interval - however testing :on_day is
kind of problem. I don’t want to duplicate any :in_interval specs -
therefore I’m trying to stub :in_interval like this:

let(:start_of_day) { Time.zone.now.beginning_of_day }
it do
Allocation.should_receive(:in_interval).with(start_of_day,
start_of_day + 1.day).and_return(“result”)

working assertion - the :in_interval stub seems to get called as

expected:

Allocation.on_day(start_of_day + 3.hours)

failing assertion:

Allocation.on_day(start_of_day + 3.hours).should == “result”
end

RSpec Output:

Failure/Error: Allocation.on_day(start_of_day + 3.hours).should ==
“result”
NoMethodError:
undefined method `includes_values’ for “result”:String

I’m using Rails 3.0.4 and RSpec 2.5 (latest versions).

Best regards,
Christoph S.

Your expectation (should_receive) is expecting “start_of_day”, which
uses Time.zone. The actual “on_day” scope does
“day.to_time.beginning_of_day”, which does not use any time zone.
Therefore, the arguments to in_interval are not the same as the
expectation. And because they are not the same, the mock does not get
set. They must be exactly the same, since you are using a specific
values.

You are not seeing a “the in_interval method was not called”
expectation ouput message because of the “includes_values” error. This
is because RSpec is comparing “result” with an array. This is because
Rails scopes return arrays, not strings (it is not returning a string
because the mock was never set).

Are you setting the time zone in a before block?

Here are two really nice gems for dealing with Time sensitive code:
https://github.com/jtrupiano/timecop

On Feb 15, 11:14am, Christoph S. [email protected] wrote:

ruby-1.8.7-p330 :004 > t.beginning_of_day
it { Interval.on_day(start_of_day + 3.hours).should == “result” }
Best regards,

“day.to_time.beginning_of_day”, which does not use any time zone.

Allocation.on_day(start_of_day + 3.hours)

http://rubyforge.org/mailman/listinfo/rspec-users


rspec-users mailing list
[email protected]://rubyforge.org/mailman/listinfo/rspec-users

Maybe because the scope is being called within a scope, it is wrapping
it in an array: [“results”]

Either way, report back what this code returns: it { raise
Interval.on_day(start_of_day + 3.hours).inspect }

Thanks for your suggestion Justin, but I don’t believe that the problem
is time zone related. Time objects usually don’t “loose” their Time Zone
when performing operations on them. Here’s an example for illustration:

$ rails console
Loading development environment (Rails 3.0.4)
ruby-1.8.7-p330 :001 > Time.zone.name
=> “Vienna”
ruby-1.8.7-p330 :002 > t = Time.zone.now.beginning_of_month
=> Tue, 01 Feb 2011 00:00:00 CET +01:00
ruby-1.8.7-p330 :003 > t += 3.hours
=> Tue, 01 Feb 2011 03:00:00 CET +01:00
ruby-1.8.7-p330 :004 > t.beginning_of_day
=> Tue, 01 Feb 2011 00:00:00 CET +01:00

If the should_receive arguments and actual arguments wouldn’t be the
same, then I would expect both examples below to fail.

context “.on_day” do
let(:start_of_day) { Time.zone.now.beginning_of_day }
before { Interval.should_receive(:in_interval).with(start_of_day,
start_of_day + 1.day).and_return(“result”) }
# (1) Passing example:
it { Interval.on_day(start_of_day + 3.hours) }
# (2) Failing example:
it { Interval.on_day(start_of_day + 3.hours).should == “result” }
end

However, (1) is passing and (2) is failing. Output as before:

Failure/Error: Allocation.on_day(start_of_day + 3.hours).should == “result”
NoMethodError:
undefined method `includes_values’ for “result”:String

Any ideas?

Best regards,
Christoph S.

So…

I settled for testing with message expectations without return values.
Guess that’s good enough for now. Thank you anyway!

Best regards,
Christoph S.