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 Schiessl
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 https://github.com/bebanjo/delorean On Feb 15, 9:35?am, Christoph Schiessl <c... at proactive.cc> wrote:> 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 Schiessl > _______________________________________________ > rspec-users mailing list > rspec-us... at rubyforge.orghttp://rubyforge.org/mailman/listinfo/rspec-users
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":StringAny ideas? Best regards, Christoph Schiessl On Feb 15, 2011, at 18:51 , Justin Ko wrote:> 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 > https://github.com/bebanjo/delorean > > On Feb 15, 9:35 am, Christoph Schiessl <c... at proactive.cc> wrote: >> 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 Schiessl >> _______________________________________________ >> rspec-users mailing list >> rspec-us... at rubyforge.orghttp://rubyforge.org/mailman/listinfo/rspec-users > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users
On Feb 15, 11:14?am, Christoph Schiessl <c... at proactive.cc> wrote:> 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 Schiessl > > On Feb 15, 2011, at 18:51 , Justin Ko wrote: > > > > > > > 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 > >https://github.com/bebanjo/delorean > > > On Feb 15, 9:35 am, Christoph Schiessl <c... at proactive.cc> wrote: > >> 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 Schiessl > >> _______________________________________________ > >> rspec-users mailing list > >> rspec-us... at rubyforge.orghttp://rubyforge.org/mailman/listinfo/rspec-users > > _______________________________________________ > > rspec-users mailing list > > rspec-us... at rubyforge.org > >http://rubyforge.org/mailman/listinfo/rspec-users > > _______________________________________________ > rspec-users mailing list > rspec-us... at rubyforge.orghttp://rubyforge.org/mailman/listinfo/rspec-usersMaybe 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 }
So... I settled for testing with message expectations without return values. Guess that''s good enough for now. Thank you anyway! Best regards, Christoph Schiessl On Feb 15, 2011, at 21:34 , Justin Ko wrote:> > > On Feb 15, 11:14 am, Christoph Schiessl <c... at proactive.cc> wrote: >> 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 Schiessl >> >> On Feb 15, 2011, at 18:51 , Justin Ko wrote: >> >> >> >> >> >>> 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 >>> https://github.com/bebanjo/delorean >> >>> On Feb 15, 9:35 am, Christoph Schiessl <c... at proactive.cc> wrote: >>>> 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 Schiessl >>>> _______________________________________________ >>>> rspec-users mailing list >>>> rspec-us... at rubyforge.orghttp://rubyforge.org/mailman/listinfo/rspec-users >>> _______________________________________________ >>> rspec-users mailing list >>> rspec-us... at rubyforge.org >>> http://rubyforge.org/mailman/listinfo/rspec-users >> >> _______________________________________________ >> rspec-users mailing list >> rspec-us... at rubyforge.orghttp://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 } > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users