Ramon Tayag
2009-Sep-16 08:34 UTC
[rspec-users] Expectation test: error says it''s different, but it''s printed the same way
I put an expectation test, but it complains two things are different when it prints it out in the error in the exact same way: http://pastie.org/618481 or below: ## the test describe ".process_aftersale" do before do @orders = [Order.make, Order.make] Order.stub!(:paid).and_return(@orders) @orders.stub!(:updated_on).and_return(@orders) end .... it "should send 1 week after sale letters" do @orders.should_receive(:updated_on).once.with([1.week.ago]) Order.process_aftersale end ... end ## The error - note that the dates are the same 1) Spec::Mocks::MockExpectationError in ''Order.process_aftersale should send 1 week after sale letters'' #<Order:0xb4e8b3a4>#<Order:0xb4e69448> expected :updated_on with ([Wed, 09 Sep 2009 14:59:59 HKT +08:00]) but received it with ([Wed, 09 Sep 2009 14:59:59 HKT +08:00]) ./spec/models/order_spec.rb:42: ## the method in Order def self.process_aftersale self.paid.updated_on(1.week.ago).each do |o| MailingsWorker.asynch_deliver_order_aftersale_to_inquire(:order_id => o.id) end end ============= I may be doing things wrong, so please let me know if I am. Thanks! Ramon Tayag
David Chelimsky
2009-Sep-16 13:08 UTC
[rspec-users] Expectation test: error says it''s different, but it''s printed the same way
On Wed, Sep 16, 2009 at 3:34 AM, Ramon Tayag <ramon.tayag at gmail.com> wrote:> I put an expectation test, but it complains two things are different > when it prints it out in the error in the exact same way: > http://pastie.org/618481Printing is printing. Time is _not_ time. The problem is that the times are off by milliseconds that aren''t accounted for in time.to_s. The usual solution is to either stub Time.now or introduce a time generator, but you''re not using Time.now, so that won''t work. Options include: 1. examine the submitted argument directly @orders.should_receive(:updated_on) do |actual| expected = 1.week.ago.to_i actual.to_i.should be_between(expected - 1000, expected) end That will expect the time within 1 second of 1.week.ago. 2. introduce a custom argument matcher class Roughly def initialize(expected) @expected = expected.to_i end def ==(actual) actual.to_i.between?(@expected - 1000, @expected) end end def roughly(expected) Roughly.new(expected) end @orders.should_receive(:updated_on).with(roughly(1.week.ago)) And for extra credit, you can even make this one more flexible and provide a lower and upper bound using a fluent interface: @orders.should_receive(:updated_on).with(within(1.second).of(1.week.ago)) HTH, David> > or below: > > ## the test > ?describe ".process_aftersale" do > ? ?before do > ? ? ?@orders = [Order.make, Order.make] > ? ? ?Order.stub!(:paid).and_return(@orders) > ? ? ?@orders.stub!(:updated_on).and_return(@orders) > ? ?end > > ? ?.... > > ? ?it "should send 1 week after sale letters" do > ? ? ?@orders.should_receive(:updated_on).once.with([1.week.ago]) > ? ? ?Order.process_aftersale > ? ?end > > ? ?... > ?end > > ## The error - note that the dates are the same > 1) > Spec::Mocks::MockExpectationError in ''Order.process_aftersale should > send 1 week after sale letters'' > #<Order:0xb4e8b3a4>#<Order:0xb4e69448> expected :updated_on with > ([Wed, 09 Sep 2009 14:59:59 HKT +08:00]) but received it with ([Wed, > 09 Sep 2009 14:59:59 HKT +08:00]) > ./spec/models/order_spec.rb:42: > > > ## the method in Order > ?def self.process_aftersale > ? ?self.paid.updated_on(1.week.ago).each do |o| > ? ? ?MailingsWorker.asynch_deliver_order_aftersale_to_inquire(:order_id > => o.id) > ? ?end > ?end > > =============> > I may be doing things wrong, so please let me know if I am. > > Thanks! > Ramon Tayag > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
J. B. Rainsberger
2009-Sep-16 17:59 UTC
[rspec-users] Expectation test: error says it''s different, but it''s printed the same way
On 2009-09-16, at 08:08 , David Chelimsky wrote:> On Wed, Sep 16, 2009 at 3:34 AM, Ramon Tayag <ramon.tayag at gmail.com> > wrote: >> I put an expectation test, but it complains two things are different >> when it prints it out in the error in the exact same way: >> http://pastie.org/618481 > > Printing is printing. Time is _not_ time. The problem is that the > times are off by milliseconds that aren''t accounted for in time.to_s. > > The usual solution is to either stub Time.now or introduce a time > generator, but you''re not using Time.now, so that won''t work. Options > include: > > 1. examine the submitted argument directly > > @orders.should_receive(:updated_on) do |actual| > expected = 1.week.ago.to_i > actual.to_i.should be_between(expected - 1000, expected) > end > > That will expect the time within 1 second of 1.week.ago. > > 2. introduce a custom argument matcher > > class Roughly > def initialize(expected) > @expected = expected.to_i > end > > def ==(actual) > actual.to_i.between?(@expected - 1000, @expected) > end > end > > def roughly(expected) > Roughly.new(expected) > end > > @orders.should_receive(:updated_on).with(roughly(1.week.ago)) > > And for extra credit, you can even make this one more flexible and > provide a lower and upper bound using a fluent interface: > > @orders.should_receive(:updated_on).with(within(1.second).of > (1.week.ago))I highly recommend a small refactoring to increase the clarity of your code and reduce the coupling with the system clock. I posted my suggestion at http://pastie.org/619142. By doing this, you make it easy to stub/expect @orders in your #process_aftersale test, and make it easy to test #ready_for_aftersale_letter without wanting to use a stub or mock. To test #ready_for_aftersale_letter, simply use 1.week.ago, 8.days.ago and 6.days.ago as your test cases. Noticing this, you probably want to try 7.days.ago + 1.millisecond and make sure that behaves the way you''d expect, because you probably want to ensure that you use 1.week.ago.at_midnight, and not just 1.week.ago. Question: if I buy from you on September 15, 2009 at 17:50, when is my order ready for an aftersale letter? (a) September 22, 2009 at 17:50, (b) September 22, 2009 at 00:00, (c) September 22, 2009 at 09:00, (d) September 23, 2009 at 00:00, or some other time entirely? Write a test for that. Take care. ---- J. B. (Joe) Rainsberger :: http://www.jbrains.ca Your guide to software craftsmanship JUnit Recipes: Practical Methods for Programmer Testing 2005 Gordon Pask Award for contributions to Agile Software Practice