Hi all, Initially I thought this was a bug in the built-in mocking framework(and it still may be), but I better hash it out on the mailing list before I file/reopen the ticket: http://rspec.lighthouseapp.com/projects/5645/tickets/478-mocks-on-constants#ticket-478-6 I thought my example illustrated my problem, but obviously I was passing the wrong arguments to the mock. I revised my example to more clearly state my problem: http://gist.github.com/2372 This is a snip of a some code from a library I''m writing. When I run this spec I get the following: # spec migration_spec.rb .F 1) Spec::Mocks::MockExpectationError in ''Migration should find the records'' Mock ''MyModel Class'' received unexpected message :count with (no args) ./migration.rb:14:in `run'' ./migration_spec.rb:19: Finished in 0.009164 seconds 2 examples, 1 failure ------------------------------------------ I want to mock out MyModel completely because it''s an ActiveRecord object. As suggested, if I define the MyModel class, this whole spec will pass. But, I don''t think I should need to do that, should I? So it seems to only occur when I assign the mock to a constant. So, even though MyModel.count is stubbed in the before block, the mock reports an unexpected message. Note however the first example passes. I had difficulty figuring out the cause of this, so I just gave FlexMock a shot (since it''s so easy to swap out). FlexMock passed both examples(not that, that alone signifies a bug in rSpec). So, my question: Is this a bug in the built in mocking framework, or am I doing it wrong? :) -Matt -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20080724/d86279d4/attachment-0001.html>
Scott Taylor
2008-Jul-25 04:00 UTC
[rspec-users] Problems with mock assigned to a constant
On Jul 24, 2008, at 11:49 PM, Matt Lins wrote:> Hi all, > > Initially I thought this was a bug in the built-in mocking > framework(and it still may be), but I better hash it out on the > mailing list before I file/reopen the ticket: > > http://rspec.lighthouseapp.com/projects/5645/tickets/478-mocks-on-constants#ticket-478-6 > > I thought my example illustrated my problem, but obviously I was > passing the wrong arguments to the mock. I revised my example to > more clearly state my problem: > > http://gist.github.com/2372 > > This is a snip of a some code from a library I''m writing. When I > run this spec I get the following: > > # spec migration_spec.rb > .F > > 1) > Spec::Mocks::MockExpectationError in ''Migration should find the > records'' > Mock ''MyModel Class'' received unexpected message :count with (no args) > ./migration.rb:14:in `run'' > ./migration_spec.rb:19: > > Finished in 0.009164 seconds > > 2 examples, 1 failure > > ------------------------------------------ > > I want to mock out MyModel completely because it''s an ActiveRecord > object. As suggested, if I define the MyModel class, this whole > spec will pass. But, I don''t think I should need to do that, should > I? So it seems to only occur when I assign the mock to a constant. > > So, even though MyModel.count is stubbed in the before block, the > mock reports an unexpected message. Note however the first example > passes.The first example passes because "should_receive" acts as a stub, too. The second example fails, because in the second example there is no stub for the count method. I''d suggest adding this line to before(:each) (or to the start of each test case): MyModel.stub!(:count).and_return 0 Scott -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20080725/80f93291/attachment.html>
Scott, Thanks, your solution does work, although I''m not sure I like it. I like to stub out behavior in my before block but also use mock expectations to verify behavior in my specs. Similar to what Dave explains here: http://blog.davidchelimsky.net/2006/11/9/tutorial-rspec-stubs-and-mocks I defined the stubs in the before block: MyModel = mock(''MyModel Class'', :count => 1, :find => [@record]) So, I''m assuming by adding the expectation in the first spec, I''m wiping out the stubs defined in the before block? If that is the case why does the first spec not fail because of MyModel.find ? Also, FlexMock does not seem to behave this way(not that they need to behave the same), but I would question whether this behavior is intentional? -Matt On Thu, Jul 24, 2008 at 11:00 PM, Scott Taylor <mailing_lists at railsnewbie.com> wrote:> > On Jul 24, 2008, at 11:49 PM, Matt Lins wrote: > > Hi all, > > Initially I thought this was a bug in the built-in mocking framework(and it still may be), but I better hash it out on the mailing list before I file/reopen the ticket: > > http://rspec.lighthouseapp.com/projects/5645/tickets/478-mocks-on-constants#ticket-478-6 > > I thought my example illustrated my problem, but obviously I was passing the wrong arguments to the mock. I revised my example to more clearly state my problem: > > http://gist.github.com/2372 > > This is a snip of a some code from a library I''m writing. When I run this spec I get the following: > > # spec migration_spec.rb > .F > > 1) > Spec::Mocks::MockExpectationError in ''Migration should find the records'' > Mock ''MyModel Class'' received unexpected message :count with (no args) > ./migration.rb:14:in `run'' > ./migration_spec.rb:19: > > Finished in 0.009164 seconds > > 2 examples, 1 failure > > ------------------------------------------ > > I want to mock out MyModel completely because it''s an ActiveRecord object. As suggested, if I define the MyModel class, this whole spec will pass. But, I don''t think I should need to do that, should I? So it seems to only occur when I assign the mock to a constant. > > So, even though MyModel.count is stubbed in the before block, the mock reports an unexpected message. Note however the first example passes. > > The first example passes because "should_receive" acts as a stub, too. > The second example fails, because in the second example there is no stub for the count method. I''d suggest adding this line to before(:each) (or to the start of each test case): > MyModel.stub!(:count).and_return 0 > Scott > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users
I suppose the way I''m defining the stubs, differs from what Dave is doing in his example. I assumed that: MyModel = mock(''MyModel Class'', :count => 1) was the same as: MyModel.stub!(:count).and_return(1) But, I''m starting to think they are not. I haven''t looked at the rSpec internals to verify, other than the parameter name: stubs_and_options+ lets you assign options and stub values at the same time. The only option available is :null_object. Anything else is treated as a stub value. So, is this problem? -Matt On Thu, Jul 24, 2008 at 11:21 PM, Matt Lins <mattlins at gmail.com> wrote:> Scott, > > Thanks, your solution does work, although I''m not sure I like it. I > like to stub out behavior in my before block but also use mock > expectations to verify behavior in my specs. Similar to what Dave > explains here: > > http://blog.davidchelimsky.net/2006/11/9/tutorial-rspec-stubs-and-mocks > > I defined the stubs in the before block: > > MyModel = mock(''MyModel Class'', :count => 1, :find => [@record]) > > So, I''m assuming by adding the expectation in the first spec, I''m > wiping out the stubs defined in the before block? If that is the case > why does the first spec not fail because of MyModel.find ? > > Also, FlexMock does not seem to behave this way(not that they need to > behave the same), but I would question whether this behavior is > intentional? > > -Matt > > On Thu, Jul 24, 2008 at 11:00 PM, Scott Taylor > <mailing_lists at railsnewbie.com> wrote: >> >> On Jul 24, 2008, at 11:49 PM, Matt Lins wrote: >> >> Hi all, >> >> Initially I thought this was a bug in the built-in mocking framework(and it still may be), but I better hash it out on the mailing list before I file/reopen the ticket: >> >> http://rspec.lighthouseapp.com/projects/5645/tickets/478-mocks-on-constants#ticket-478-6 >> >> I thought my example illustrated my problem, but obviously I was passing the wrong arguments to the mock. I revised my example to more clearly state my problem: >> >> http://gist.github.com/2372 >> >> This is a snip of a some code from a library I''m writing. When I run this spec I get the following: >> >> # spec migration_spec.rb >> .F >> >> 1) >> Spec::Mocks::MockExpectationError in ''Migration should find the records'' >> Mock ''MyModel Class'' received unexpected message :count with (no args) >> ./migration.rb:14:in `run'' >> ./migration_spec.rb:19: >> >> Finished in 0.009164 seconds >> >> 2 examples, 1 failure >> >> ------------------------------------------ >> >> I want to mock out MyModel completely because it''s an ActiveRecord object. As suggested, if I define the MyModel class, this whole spec will pass. But, I don''t think I should need to do that, should I? So it seems to only occur when I assign the mock to a constant. >> >> So, even though MyModel.count is stubbed in the before block, the mock reports an unexpected message. Note however the first example passes. >> >> The first example passes because "should_receive" acts as a stub, too. >> The second example fails, because in the second example there is no stub for the count method. I''d suggest adding this line to before(:each) (or to the start of each test case): >> MyModel.stub!(:count).and_return 0 >> Scott >> >> _______________________________________________ >> rspec-users mailing list >> rspec-users at rubyforge.org >> http://rubyforge.org/mailman/listinfo/rspec-users >
Scott Taylor
2008-Jul-25 04:38 UTC
[rspec-users] Problems with mock assigned to a constant
On Jul 25, 2008, at 12:21 AM, Matt Lins wrote:> Scott, > > Thanks, your solution does work, although I''m not sure I like it. I > like to stub out behavior in my before block but also use mock > expectations to verify behavior in my specs. Similar to what Dave > explains here: > > http://blog.davidchelimsky.net/2006/11/9/tutorial-rspec-stubs-and- > mocks > > I defined the stubs in the before block: > > MyModel = mock(''MyModel Class'', :count => 1, :find => [@record]) >Ah - well, I missed this part. This make much more sense. Btw, aren''t you seeing warnings every time you run your specs? Redefining the constant for your test, is, IMHO, the most ugly solution you can take (and plus, it''ll break in many circumstances - for instance, it probably wont'' play well with rails loading schemes). One way around this is by using dependency injection - I would highly recommend you use this technique. *DON''T* use the constant technique, unless you really know what your doing. Unfortunately there are times when DI doesn''t work (especially in the rails world) - in those cases, you really have no other option besides stubbing the class methods directly.> wiping out the stubs defined in the before block? If that is the case > why does the first spec not fail because of MyModel.find ?Well - what is happening between each test case? Are the classes (defined elsewhere) being reloaded each time? Are you getting a warning when you redefine MyModel? Do you understand how rails is (re)loading this stuff? Scott
Scott Taylor
2008-Jul-25 04:47 UTC
[rspec-users] Problems with mock assigned to a constant
On Jul 25, 2008, at 12:32 AM, Matt Lins wrote:> I suppose the way I''m defining the stubs, differs from what Dave is > doing in his example. > > I assumed that: > > MyModel = mock(''MyModel Class'', :count => 1) > > was the same as: > > MyModel.stub!(:count).and_return(1)Nope. Not even close. Here''s an equivalent of the first form: Object.send :remove_const, :MyModel MyModel = <a mock object> and here''s the second form: MyModel.instance_eval do def count 1 end end (or:) MyModel.class_eval do class << self def count; 1; end end end Scott> > > But, I''m starting to think they are not. I haven''t looked at the > rSpec internals to verify, other than the parameter name: > > stubs_and_options+ lets you assign options and stub values > at the same time. The only option available is :null_object. > Anything else is treated as a stub value. > > So, is this problem?Yeah - so here are two related, but not equivalent ideas: mock objects, and stubs. A stub is just a faked out method - it can exist on a mock object (a completely fake object), or on a partial mock (i.e. a real object, with a method faked out). mock(''My mock") is a mock object, MyRealObject.stub!(:foo) is a real object with the method foo faked out. What is the difference between a mock object and a fake object? A mock object will complain (read: raise an error) any time it receives a message which it doesn''t understand (i.e. one which hasn''t been explicitly stubbed). A real object will work as usual. (A null object mock is a special type of mock - one which never complains. For now, you shouldn''t worry about it). Hope this helps, Scott
Scott Taylor
2008-Jul-25 04:51 UTC
[rspec-users] Problems with mock assigned to a constant
On Jul 25, 2008, at 12:47 AM, Scott Taylor wrote:> > On Jul 25, 2008, at 12:32 AM, Matt Lins wrote: > >> I suppose the way I''m defining the stubs, differs from what Dave is >> doing in his example. >> >> I assumed that: >> >> MyModel = mock(''MyModel Class'', :count => 1) >> >> was the same as: >> >> MyModel.stub!(:count).and_return(1) > > Nope. Not even close. Here''s an equivalent of the first form: > > Object.send :remove_const, :MyModel > MyModel = <a mock object> > > and here''s the second form: > > MyModel.instance_eval do > def count > 1 > end > end > > (or:) > > MyModel.class_eval do > class << self > def count; 1; end > end > end > > Scott > > >> >> >> But, I''m starting to think they are not. I haven''t looked at the >> rSpec internals to verify, other than the parameter name: >> >> stubs_and_options+ lets you assign options and stub values >> at the same time. The only option available is :null_object. >> Anything else is treated as a stub value. >> >> So, is this problem? > > Yeah - so here are two related, but not equivalent ideas: mock > objects, and stubs. A stub is just a faked out method - it can > exist on a mock object (a completely fake object), or on a partial > mock (i.e. a real object, with a method faked out). mock(''My mock") > is a mock object, MyRealObject.stub!(:foo) is a real object with the > method foo faked out. > > What is the difference between a mock object and a fake object? A > mock object will complain (read: raise an error) any time it > receives a message which it doesn''t understand (i.e. one which > hasn''t been explicitly stubbed). A real object will work as usual. > (A null object mock is a special type of mock - one which never > complains. For now, you shouldn''t worry about it). > > Hope this helps, > > Scott > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-usersOops. Guess I signed that one twice. I''ll make up for it by not signing this one at all.
On Thu, Jul 24, 2008 at 11:38 PM, Scott Taylor <mailing_lists at railsnewbie.com> wrote:> > On Jul 25, 2008, at 12:21 AM, Matt Lins wrote: > >> Scott, >> >> Thanks, your solution does work, although I''m not sure I like it. I >> like to stub out behavior in my before block but also use mock >> expectations to verify behavior in my specs. Similar to what Dave >> explains here: >> >> http://blog.davidchelimsky.net/2006/11/9/tutorial-rspec-stubs-and-mocks >> >> I defined the stubs in the before block: >> >> MyModel = mock(''MyModel Class'', :count => 1, :find => [@record]) >> > > Ah - well, I missed this part. This make much more sense. > > Btw, aren''t you seeing warnings every time you run your specs?I''m not sure what code you''re looking at, but in the gist paste I posted for this thread, I''m only defining the constant once( before(:all) ). If you''re looking at the lighthouse code, I was undefining the constants in the after block. So, no, I''m not seeing warnings.> > Redefining the constant for your test, is, IMHO, the most ugly solution you > can take (and plus, it''ll break in many circumstances - for instance, it > probably wont'' play well with rails loading schemes). One way around this > is by using dependency injection - I would highly recommend you use this > technique. *DON''T* use the constant technique, unless you really know what > your doing. >Like I said I''m not redefining the constants. Thanks for the insight, I''ll research DI.> Unfortunately there are times when DI doesn''t work (especially in the rails > world) - in those cases, you really have no other option besides stubbing > the class methods directly. > >> wiping out the stubs defined in the before block? If that is the case >> why does the first spec not fail because of MyModel.find ? > > Well - what is happening between each test case? Are the classes (defined > elsewhere) being reloaded each time? Are you getting a warning when you > redefine MyModel? Do you understand how rails is (re)loading this stuff? >All my code for the example was posted in gist ( you can download it if you''d like ). I''m not using Rails, I''m writing a library that uses ActiveRecord. But, all that is mocked out. The example in gist fully illustrates the problem in my actual library. The output that I posted in the first post is exactly what you get if you run the code pasted in gist: http://gist.github.com/2372 Sorry, if that was confusing, I shouldn''t have even mentioned the lighthouse ticket.> Scott > > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
On Thu, Jul 24, 2008 at 11:47 PM, Scott Taylor <mailing_lists at railsnewbie.com> wrote:> > On Jul 25, 2008, at 12:32 AM, Matt Lins wrote: > >> I suppose the way I''m defining the stubs, differs from what Dave is >> doing in his example. >> >> I assumed that: >> >> MyModel = mock(''MyModel Class'', :count => 1) >> >> was the same as: >> >> MyModel.stub!(:count).and_return(1) > > Nope. Not even close. Here''s an equivalent of the first form: > > Object.send :remove_const, :MyModel > MyModel = <a mock object> > > and here''s the second form: > > MyModel.instance_eval do > def count > 1 > end > end > > (or:) > > MyModel.class_eval do > class << self > def count; 1; end > end > end > > Scott > >But the stubs are defined the same way in both occurrences, no? MyModel = mock(''MyModel Class'', :count => 1) By passing {:count => 1} to +stubs_and_options+ I should have defined stubs on the mock object. I''m using it as a shortcut for this: MyModel = mock(''MyModel Class'') MyModel.stub!(:count).and_return(1) If those example aren''t doing the exact same thing I guess I''m a little baffled (or maybe just need to go to sleep).>> >> >> But, I''m starting to think they are not. I haven''t looked at the >> rSpec internals to verify, other than the parameter name: >> >> stubs_and_options+ lets you assign options and stub values >> at the same time. The only option available is :null_object. >> Anything else is treated as a stub value. >> >> So, is this problem? > > Yeah - so here are two related, but not equivalent ideas: mock objects, and > stubs. A stub is just a faked out method - it can exist on a mock object (a > completely fake object), or on a partial mock (i.e. a real object, with a > method faked out). mock(''My mock") is a mock object, > MyRealObject.stub!(:foo) is a real object with the method foo faked out. > > What is the difference between a mock object and a fake object? A mock > object will complain (read: raise an error) any time it receives a message > which it doesn''t understand (i.e. one which hasn''t been explicitly stubbed). > A real object will work as usual. (A null object mock is a special type of > mock - one which never complains. For now, you shouldn''t worry about it). >Ok, I get what you saying, but as I understand it I am explicitly stubbing out the methods on the _mock_ object and it''s still complaining. If +stubs_and_options+ isn''t stubbing, then what is it doing?> Hope this helps, > > Scott > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
Scott Taylor
2008-Jul-25 05:16 UTC
[rspec-users] Problems with mock assigned to a constant
On Jul 25, 2008, at 1:05 AM, Matt Lins wrote:> On Thu, Jul 24, 2008 at 11:38 PM, Scott Taylor > <mailing_lists at railsnewbie.com> wrote: >> >> On Jul 25, 2008, at 12:21 AM, Matt Lins wrote: >> >>> Scott, >>> >>> Thanks, your solution does work, although I''m not sure I like it. I >>> like to stub out behavior in my before block but also use mock >>> expectations to verify behavior in my specs. Similar to what Dave >>> explains here: >>> >>> http://blog.davidchelimsky.net/2006/11/9/tutorial-rspec-stubs-and-mocks >>> >>> I defined the stubs in the before block: >>> >>> MyModel = mock(''MyModel Class'', :count => 1, :find => [@record]) >>> >> >> Ah - well, I missed this part. This make much more sense. >> >> Btw, aren''t you seeing warnings every time you run your specs? > > I''m not sure what code you''re looking at, but in the gist paste I > posted for this thread, I''m only defining the constant once( > before(:all) ). If you''re looking at the lighthouse code, I was > undefining the constants in the after block. So, no, I''m not seeing > warnings. > >> >> Redefining the constant for your test, is, IMHO, the most ugly >> solution you >> can take (and plus, it''ll break in many circumstances - for >> instance, it >> probably wont'' play well with rails loading schemes). One way >> around this >> is by using dependency injection - I would highly recommend you use >> this >> technique. *DON''T* use the constant technique, unless you really >> know what >> your doing. >> > > Like I said I''m not redefining the constants. Thanks for the insight, > I''ll research DI. > >> Unfortunately there are times when DI doesn''t work (especially in >> the rails >> world) - in those cases, you really have no other option besides >> stubbing >> the class methods directly. >> >>> wiping out the stubs defined in the before block? If that is the >>> case >>> why does the first spec not fail because of MyModel.find ? >> >> Well - what is happening between each test case? Are the classes >> (defined >> elsewhere) being reloaded each time? Are you getting a warning >> when you >> redefine MyModel? Do you understand how rails is (re)loading this >> stuff? >> > > All my code for the example was posted in gist ( you can download it > if you''d like ). I''m not using Rails, I''m writing a library that uses > ActiveRecord. But, all that is mocked out. The example in gist fully > illustrates the problem in my actual library. The output that I > posted in the first post is exactly what you get if you run the code > pasted in gist: > > http://gist.github.com/2372 > > Sorry, if that was confusing, I shouldn''t have even mentioned the > lighthouse ticket. >No - it was my own fault. I didn''t take the time to read your post fully. Anyway - DI is surely the way to go. And generally you''ll want to stay away from before(:all) when you can. Best, Scott
Scott Taylor
2008-Jul-25 05:25 UTC
[rspec-users] Problems with mock assigned to a constant
On Jul 25, 2008, at 1:15 AM, Matt Lins wrote:> On Thu, Jul 24, 2008 at 11:47 PM, Scott Taylor > <mailing_lists at railsnewbie.com> wrote: >> >> On Jul 25, 2008, at 12:32 AM, Matt Lins wrote: >> >>> I suppose the way I''m defining the stubs, differs from what Dave is >>> doing in his example. >>> >>> I assumed that: >>> >>> MyModel = mock(''MyModel Class'', :count => 1) >>> >>> was the same as: >>> >>> MyModel.stub!(:count).and_return(1) >> >> Nope. Not even close. Here''s an equivalent of the first form: >> >> Object.send :remove_const, :MyModel >> MyModel = <a mock object> >> >> and here''s the second form: >> >> MyModel.instance_eval do >> def count >> 1 >> end >> end >> >> (or:) >> >> MyModel.class_eval do >> class << self >> def count; 1; end >> end >> end >> >> Scott >> >> > > But the stubs are defined the same way in both occurrences, no? > > MyModel = mock(''MyModel Class'', :count => 1) > > By passing {:count => 1} to +stubs_and_options+ I should have defined > stubs on the mock object. I''m using it as a shortcut for this: > > MyModel = mock(''MyModel Class'') > MyModel.stub!(:count).and_return(1) > > If those example aren''t doing the exact same thing I guess I''m a > little baffled (or maybe just need to go to sleep).The first one is redefining the constant ''MyModel''. The second one is just redefining a class method (the constant isn''t changing - it''s remaining whatever it was before - say, a class)> > >>> >>> >>> But, I''m starting to think they are not. I haven''t looked at the >>> rSpec internals to verify, other than the parameter name: >>> >>> stubs_and_options+ lets you assign options and stub values >>> at the same time. The only option available is :null_object. >>> Anything else is treated as a stub value. >>> >>> So, is this problem? >> >> Yeah - so here are two related, but not equivalent ideas: mock >> objects, and >> stubs. A stub is just a faked out method - it can exist on a mock >> object (a >> completely fake object), or on a partial mock (i.e. a real object, >> with a >> method faked out). mock(''My mock") is a mock object, >> MyRealObject.stub!(:foo) is a real object with the method foo faked >> out. >> >> What is the difference between a mock object and a fake object? A >> mock >> object will complain (read: raise an error) any time it receives a >> message >> which it doesn''t understand (i.e. one which hasn''t been explicitly >> stubbed). >> A real object will work as usual. (A null object mock is a special >> type of >> mock - one which never complains. For now, you shouldn''t worry >> about it). >> > > Ok, I get what you saying, but as I understand it I am explicitly > stubbing out the methods on the _mock_ object and it''s still > complaining. If +stubs_and_options+ isn''t stubbing, then what is it > doing?That''s right - the hash to the mock method is a shorthand. So these two are equivalent: my_mock = mock(''a mock'') my_mock.stub!(:foo).and_return(:bar) AND: my_mock = mock("a mock", {:foo => :bar}) Scott
On Fri, Jul 25, 2008 at 12:25 AM, Scott Taylor <mailing_lists at railsnewbie.com> wrote:> > On Jul 25, 2008, at 1:15 AM, Matt Lins wrote: > >> On Thu, Jul 24, 2008 at 11:47 PM, Scott Taylor >> <mailing_lists at railsnewbie.com> wrote: >>> >>> On Jul 25, 2008, at 12:32 AM, Matt Lins wrote: >>> >>>> I suppose the way I''m defining the stubs, differs from what Dave is >>>> doing in his example. >>>> >>>> I assumed that: >>>> >>>> MyModel = mock(''MyModel Class'', :count => 1) >>>> >>>> was the same as: >>>> >>>> MyModel.stub!(:count).and_return(1) >>> >>> Nope. Not even close. Here''s an equivalent of the first form: >>> >>> Object.send :remove_const, :MyModel >>> MyModel = <a mock object> >>> >>> and here''s the second form: >>> >>> MyModel.instance_eval do >>> def count >>> 1 >>> end >>> end >>> >>> (or:) >>> >>> MyModel.class_eval do >>> class << self >>> def count; 1; end >>> end >>> end >>> >>> Scott >>> >>> >> >> But the stubs are defined the same way in both occurrences, no? >> >> MyModel = mock(''MyModel Class'', :count => 1) >> >> By passing {:count => 1} to +stubs_and_options+ I should have defined >> stubs on the mock object. I''m using it as a shortcut for this: >> >> MyModel = mock(''MyModel Class'') >> MyModel.stub!(:count).and_return(1) >> >> If those example aren''t doing the exact same thing I guess I''m a >> little baffled (or maybe just need to go to sleep). > > The first one is redefining the constant ''MyModel''. The second one is just > redefining a class method (the constant isn''t changing - it''s remaining > whatever it was before - say, a class) > >> >> >>>> >>>> >>>> But, I''m starting to think they are not. I haven''t looked at the >>>> rSpec internals to verify, other than the parameter name: >>>> >>>> stubs_and_options+ lets you assign options and stub values >>>> at the same time. The only option available is :null_object. >>>> Anything else is treated as a stub value. >>>> >>>> So, is this problem? >>> >>> Yeah - so here are two related, but not equivalent ideas: mock objects, >>> and >>> stubs. A stub is just a faked out method - it can exist on a mock object >>> (a >>> completely fake object), or on a partial mock (i.e. a real object, with a >>> method faked out). mock(''My mock") is a mock object, >>> MyRealObject.stub!(:foo) is a real object with the method foo faked out. >>> >>> What is the difference between a mock object and a fake object? A mock >>> object will complain (read: raise an error) any time it receives a >>> message >>> which it doesn''t understand (i.e. one which hasn''t been explicitly >>> stubbed). >>> A real object will work as usual. (A null object mock is a special type >>> of >>> mock - one which never complains. For now, you shouldn''t worry about >>> it). >>> >> >> Ok, I get what you saying, but as I understand it I am explicitly >> stubbing out the methods on the _mock_ object and it''s still >> complaining. If +stubs_and_options+ isn''t stubbing, then what is it >> doing? > > That''s right - the hash to the mock method is a shorthand. So these two are > equivalent: > > my_mock = mock(''a mock'') > my_mock.stub!(:foo).and_return(:bar) > > AND: > > my_mock = mock("a mock", {:foo => :bar}) > > Scott > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >Ok, then would you still insist that: This: http://gist.github.com/2372 Should produce this: # spec migration_spec.rb .F 1) Spec::Mocks::MockExpectationError in ''Migration should find the records'' Mock ''MyModel Class'' received unexpected message :count with (no args) ./migration.rb:14:in `run'' ./migration_spec.rb:19: Finished in 0.009435 seconds -------------------- And like I said earlier, this code passes both examples with FlexMock( if you simply replace mock with flexmock and uncomment the code in spec_helper, of course you need the flexmock gem)
David Chelimsky
2008-Jul-25 12:57 UTC
[rspec-users] Problems with mock assigned to a constant
On Fri, Jul 25, 2008 at 12:34 AM, Matt Lins <mattlins at gmail.com> wrote:> On Fri, Jul 25, 2008 at 12:25 AM, Scott Taylor > <mailing_lists at railsnewbie.com> wrote: >> >> On Jul 25, 2008, at 1:15 AM, Matt Lins wrote: >> >>> On Thu, Jul 24, 2008 at 11:47 PM, Scott Taylor >>> <mailing_lists at railsnewbie.com> wrote: >>>> >>>> On Jul 25, 2008, at 12:32 AM, Matt Lins wrote: >>>> >>>>> I suppose the way I''m defining the stubs, differs from what Dave is >>>>> doing in his example. >>>>> >>>>> I assumed that: >>>>> >>>>> MyModel = mock(''MyModel Class'', :count => 1) >>>>> >>>>> was the same as: >>>>> >>>>> MyModel.stub!(:count).and_return(1) >>>> >>>> Nope. Not even close. Here''s an equivalent of the first form: >>>> >>>> Object.send :remove_const, :MyModel >>>> MyModel = <a mock object> >>>> >>>> and here''s the second form: >>>> >>>> MyModel.instance_eval do >>>> def count >>>> 1 >>>> end >>>> end >>>> >>>> (or:) >>>> >>>> MyModel.class_eval do >>>> class << self >>>> def count; 1; end >>>> end >>>> end >>>> >>>> Scott >>>> >>>> >>> >>> But the stubs are defined the same way in both occurrences, no? >>> >>> MyModel = mock(''MyModel Class'', :count => 1) >>> >>> By passing {:count => 1} to +stubs_and_options+ I should have defined >>> stubs on the mock object. I''m using it as a shortcut for this: >>> >>> MyModel = mock(''MyModel Class'') >>> MyModel.stub!(:count).and_return(1) >>> >>> If those example aren''t doing the exact same thing I guess I''m a >>> little baffled (or maybe just need to go to sleep). >> >> The first one is redefining the constant ''MyModel''. The second one is just >> redefining a class method (the constant isn''t changing - it''s remaining >> whatever it was before - say, a class) >> >>> >>> >>>>> >>>>> >>>>> But, I''m starting to think they are not. I haven''t looked at the >>>>> rSpec internals to verify, other than the parameter name: >>>>> >>>>> stubs_and_options+ lets you assign options and stub values >>>>> at the same time. The only option available is :null_object. >>>>> Anything else is treated as a stub value. >>>>> >>>>> So, is this problem? >>>> >>>> Yeah - so here are two related, but not equivalent ideas: mock objects, >>>> and >>>> stubs. A stub is just a faked out method - it can exist on a mock object >>>> (a >>>> completely fake object), or on a partial mock (i.e. a real object, with a >>>> method faked out). mock(''My mock") is a mock object, >>>> MyRealObject.stub!(:foo) is a real object with the method foo faked out. >>>> >>>> What is the difference between a mock object and a fake object? A mock >>>> object will complain (read: raise an error) any time it receives a >>>> message >>>> which it doesn''t understand (i.e. one which hasn''t been explicitly >>>> stubbed). >>>> A real object will work as usual. (A null object mock is a special type >>>> of >>>> mock - one which never complains. For now, you shouldn''t worry about >>>> it). >>>> >>> >>> Ok, I get what you saying, but as I understand it I am explicitly >>> stubbing out the methods on the _mock_ object and it''s still >>> complaining. If +stubs_and_options+ isn''t stubbing, then what is it >>> doing? >> >> That''s right - the hash to the mock method is a shorthand. So these two are >> equivalent: >> >> my_mock = mock(''a mock'') >> my_mock.stub!(:foo).and_return(:bar) >> >> AND: >> >> my_mock = mock("a mock", {:foo => :bar}) >> >> Scott >> >> _______________________________________________ >> rspec-users mailing list >> rspec-users at rubyforge.org >> http://rubyforge.org/mailman/listinfo/rspec-users >> > > Ok, then would you still insist that: > > This: > > http://gist.github.com/2372 > > Should produce this: > > # spec migration_spec.rb > .F > > 1) > Spec::Mocks::MockExpectationError in ''Migration should find the records'' > Mock ''MyModel Class'' received unexpected message :count with (no args) > ./migration.rb:14:in `run'' > ./migration_spec.rb:19: > > Finished in 0.009435 seconds > > -------------------- > > And like I said earlier, this code passes both examples with FlexMock( > if you simply replace mock with flexmock and uncomment the code in > spec_helper, of course you need the flexmock gem)I can''t speak for why it''s passing in Flexmock, but I can explain why it''s failing in rspec. RSpec clears out all stub methods and message expectations at the end of each example. In this case, the stub on count is defined in a before(:all) block, which is only executed once, before all the examples are run (perhaps before(:any) would be a more clear expression of this?). After the first example is executed, that stub goes away. So when the mock receives the :count message in the second example, it''s not expecting it (which is exactly what it''s telling you). If you run the second example by itself (spec migration_spec.rb -e "should find the records") it will pass. You can solve the immediate problem by removing the stubs from the initial declaration of the MyModel constant and moving them to a before(:each) block so they get set before each example. Another option is to set :null_object => true. That will tell the mock to ignore unexpected messages, however the stub on find might still need to move to before(:each) because it returns something. Also - this code creates instance variables that get used across examples. If something happens in the first example to change the state of @record, you''re going to get the same object in the second example and it becomes a challenge to understand what''s happening when there are failures in the second example. I don''t use before(:all) blocks this way for exactly this reason. They are run only once, and can cause a lot of confusion because they leak state across examples. The way I usually go about something like this is to create a simple empty class: class MyModel; end And then set expectations on it before(:each) example. You can get the gist of what I''m talking about here: http://gist.github.com/2438 - I''ve got two different approaches in two separate commits, so grab the repo to see both (this is my first time checking out gist - wow!). HTH, David
David Chelimsky
2008-Jul-25 13:15 UTC
[rspec-users] Problems with mock assigned to a constant
On Fri, Jul 25, 2008 at 7:57 AM, David Chelimsky <dchelimsky at gmail.com> wrote:> On Fri, Jul 25, 2008 at 12:34 AM, Matt Lins <mattlins at gmail.com> wrote: >> On Fri, Jul 25, 2008 at 12:25 AM, Scott Taylor >> <mailing_lists at railsnewbie.com> wrote: >>> >>> On Jul 25, 2008, at 1:15 AM, Matt Lins wrote: >>> >>>> On Thu, Jul 24, 2008 at 11:47 PM, Scott Taylor >>>> <mailing_lists at railsnewbie.com> wrote: >>>>> >>>>> On Jul 25, 2008, at 12:32 AM, Matt Lins wrote: >>>>> >>>>>> I suppose the way I''m defining the stubs, differs from what Dave is >>>>>> doing in his example. >>>>>> >>>>>> I assumed that: >>>>>> >>>>>> MyModel = mock(''MyModel Class'', :count => 1) >>>>>> >>>>>> was the same as: >>>>>> >>>>>> MyModel.stub!(:count).and_return(1) >>>>> >>>>> Nope. Not even close. Here''s an equivalent of the first form: >>>>> >>>>> Object.send :remove_const, :MyModel >>>>> MyModel = <a mock object> >>>>> >>>>> and here''s the second form: >>>>> >>>>> MyModel.instance_eval do >>>>> def count >>>>> 1 >>>>> end >>>>> end >>>>> >>>>> (or:) >>>>> >>>>> MyModel.class_eval do >>>>> class << self >>>>> def count; 1; end >>>>> end >>>>> end >>>>> >>>>> Scott >>>>> >>>>> >>>> >>>> But the stubs are defined the same way in both occurrences, no? >>>> >>>> MyModel = mock(''MyModel Class'', :count => 1) >>>> >>>> By passing {:count => 1} to +stubs_and_options+ I should have defined >>>> stubs on the mock object. I''m using it as a shortcut for this: >>>> >>>> MyModel = mock(''MyModel Class'') >>>> MyModel.stub!(:count).and_return(1) >>>> >>>> If those example aren''t doing the exact same thing I guess I''m a >>>> little baffled (or maybe just need to go to sleep). >>> >>> The first one is redefining the constant ''MyModel''. The second one is just >>> redefining a class method (the constant isn''t changing - it''s remaining >>> whatever it was before - say, a class) >>> >>>> >>>> >>>>>> >>>>>> >>>>>> But, I''m starting to think they are not. I haven''t looked at the >>>>>> rSpec internals to verify, other than the parameter name: >>>>>> >>>>>> stubs_and_options+ lets you assign options and stub values >>>>>> at the same time. The only option available is :null_object. >>>>>> Anything else is treated as a stub value. >>>>>> >>>>>> So, is this problem? >>>>> >>>>> Yeah - so here are two related, but not equivalent ideas: mock objects, >>>>> and >>>>> stubs. A stub is just a faked out method - it can exist on a mock object >>>>> (a >>>>> completely fake object), or on a partial mock (i.e. a real object, with a >>>>> method faked out). mock(''My mock") is a mock object, >>>>> MyRealObject.stub!(:foo) is a real object with the method foo faked out. >>>>> >>>>> What is the difference between a mock object and a fake object? A mock >>>>> object will complain (read: raise an error) any time it receives a >>>>> message >>>>> which it doesn''t understand (i.e. one which hasn''t been explicitly >>>>> stubbed). >>>>> A real object will work as usual. (A null object mock is a special type >>>>> of >>>>> mock - one which never complains. For now, you shouldn''t worry about >>>>> it). >>>>> >>>> >>>> Ok, I get what you saying, but as I understand it I am explicitly >>>> stubbing out the methods on the _mock_ object and it''s still >>>> complaining. If +stubs_and_options+ isn''t stubbing, then what is it >>>> doing? >>> >>> That''s right - the hash to the mock method is a shorthand. So these two are >>> equivalent: >>> >>> my_mock = mock(''a mock'') >>> my_mock.stub!(:foo).and_return(:bar) >>> >>> AND: >>> >>> my_mock = mock("a mock", {:foo => :bar}) >>> >>> Scott >>> >>> _______________________________________________ >>> rspec-users mailing list >>> rspec-users at rubyforge.org >>> http://rubyforge.org/mailman/listinfo/rspec-users >>> >> >> Ok, then would you still insist that: >> >> This: >> >> http://gist.github.com/2372 >> >> Should produce this: >> >> # spec migration_spec.rb >> .F >> >> 1) >> Spec::Mocks::MockExpectationError in ''Migration should find the records'' >> Mock ''MyModel Class'' received unexpected message :count with (no args) >> ./migration.rb:14:in `run'' >> ./migration_spec.rb:19: >> >> Finished in 0.009435 seconds >> >> -------------------- >> >> And like I said earlier, this code passes both examples with FlexMock( >> if you simply replace mock with flexmock and uncomment the code in >> spec_helper, of course you need the flexmock gem) > > I can''t speak for why it''s passing in Flexmock, but I can explain why > it''s failing in rspec. > > RSpec clears out all stub methods and message expectations at the end > of each example. In this case, the stub on count is defined in a > before(:all) block, which is only executed once, before all the > examples are run (perhaps before(:any) would be a more clear > expression of this?). After the first example is executed, that stub > goes away. So when the mock receives the :count message in the second > example, it''s not expecting it (which is exactly what it''s telling > you). If you run the second example by itself (spec migration_spec.rb > -e "should find the records") it will pass. > > You can solve the immediate problem by removing the stubs from the > initial declaration of the MyModel constant and moving them to a > before(:each) block so they get set before each example. > > Another option is to set :null_object => true. That will tell the mock > to ignore unexpected messages, however the stub on find might still > need to move to before(:each) because it returns something. > > Also - this code creates instance variables that get used across > examples. If something happens in the first example to change the > state of @record, you''re going to get the same object in the second > example and it becomes a challenge to understand what''s happening when > there are failures in the second example. > > I don''t use before(:all) blocks this way for exactly this reason. They > are run only once, and can cause a lot of confusion because they leak > state across examples. The way I usually go about something like this > is to create a simple empty class: > > class MyModel; end > > And then set expectations on it before(:each) example. > > You can get the gist of what I''m talking about here: > http://gist.github.com/2438 - I''ve got two different approaches in two > separate commits, so grab the repo to see bothOr you *could* just look at them on line! https://gist.github.com/2438/040f26916032ad864ba51d0d733e16056c77be42 https://gist.github.com/2438/0ee4fcaebbafdbdab77dffd5228a9aae92f17191> (this is my first time > checking out gist - wow!). > > HTH, > David >
Yes, gist is great! Thank you very much for taking the time to look at this. I like your suggestions very much and will use them. At this point I''m just messing around, but I don''t understand why this doesn''t work. One more bad implementation if you have time: http://gist.github.com/2372 I''m removing the constant after each spec runs and redefining it before each runs. The second spec still doesn''t pass though, even though the constant is defined before each spec. Again, I realize this is horrible, but it should still work, no? On Fri, Jul 25, 2008 at 8:15 AM, David Chelimsky <dchelimsky at gmail.com> wrote:> On Fri, Jul 25, 2008 at 7:57 AM, David Chelimsky <dchelimsky at gmail.com> wrote: >> On Fri, Jul 25, 2008 at 12:34 AM, Matt Lins <mattlins at gmail.com> wrote: >>> On Fri, Jul 25, 2008 at 12:25 AM, Scott Taylor >>> <mailing_lists at railsnewbie.com> wrote: >>>> >>>> On Jul 25, 2008, at 1:15 AM, Matt Lins wrote: >>>> >>>>> On Thu, Jul 24, 2008 at 11:47 PM, Scott Taylor >>>>> <mailing_lists at railsnewbie.com> wrote: >>>>>> >>>>>> On Jul 25, 2008, at 12:32 AM, Matt Lins wrote: >>>>>> >>>>>>> I suppose the way I''m defining the stubs, differs from what Dave is >>>>>>> doing in his example. >>>>>>> >>>>>>> I assumed that: >>>>>>> >>>>>>> MyModel = mock(''MyModel Class'', :count => 1) >>>>>>> >>>>>>> was the same as: >>>>>>> >>>>>>> MyModel.stub!(:count).and_return(1) >>>>>> >>>>>> Nope. Not even close. Here''s an equivalent of the first form: >>>>>> >>>>>> Object.send :remove_const, :MyModel >>>>>> MyModel = <a mock object> >>>>>> >>>>>> and here''s the second form: >>>>>> >>>>>> MyModel.instance_eval do >>>>>> def count >>>>>> 1 >>>>>> end >>>>>> end >>>>>> >>>>>> (or:) >>>>>> >>>>>> MyModel.class_eval do >>>>>> class << self >>>>>> def count; 1; end >>>>>> end >>>>>> end >>>>>> >>>>>> Scott >>>>>> >>>>>> >>>>> >>>>> But the stubs are defined the same way in both occurrences, no? >>>>> >>>>> MyModel = mock(''MyModel Class'', :count => 1) >>>>> >>>>> By passing {:count => 1} to +stubs_and_options+ I should have defined >>>>> stubs on the mock object. I''m using it as a shortcut for this: >>>>> >>>>> MyModel = mock(''MyModel Class'') >>>>> MyModel.stub!(:count).and_return(1) >>>>> >>>>> If those example aren''t doing the exact same thing I guess I''m a >>>>> little baffled (or maybe just need to go to sleep). >>>> >>>> The first one is redefining the constant ''MyModel''. The second one is just >>>> redefining a class method (the constant isn''t changing - it''s remaining >>>> whatever it was before - say, a class) >>>> >>>>> >>>>> >>>>>>> >>>>>>> >>>>>>> But, I''m starting to think they are not. I haven''t looked at the >>>>>>> rSpec internals to verify, other than the parameter name: >>>>>>> >>>>>>> stubs_and_options+ lets you assign options and stub values >>>>>>> at the same time. The only option available is :null_object. >>>>>>> Anything else is treated as a stub value. >>>>>>> >>>>>>> So, is this problem? >>>>>> >>>>>> Yeah - so here are two related, but not equivalent ideas: mock objects, >>>>>> and >>>>>> stubs. A stub is just a faked out method - it can exist on a mock object >>>>>> (a >>>>>> completely fake object), or on a partial mock (i.e. a real object, with a >>>>>> method faked out). mock(''My mock") is a mock object, >>>>>> MyRealObject.stub!(:foo) is a real object with the method foo faked out. >>>>>> >>>>>> What is the difference between a mock object and a fake object? A mock >>>>>> object will complain (read: raise an error) any time it receives a >>>>>> message >>>>>> which it doesn''t understand (i.e. one which hasn''t been explicitly >>>>>> stubbed). >>>>>> A real object will work as usual. (A null object mock is a special type >>>>>> of >>>>>> mock - one which never complains. For now, you shouldn''t worry about >>>>>> it). >>>>>> >>>>> >>>>> Ok, I get what you saying, but as I understand it I am explicitly >>>>> stubbing out the methods on the _mock_ object and it''s still >>>>> complaining. If +stubs_and_options+ isn''t stubbing, then what is it >>>>> doing? >>>> >>>> That''s right - the hash to the mock method is a shorthand. So these two are >>>> equivalent: >>>> >>>> my_mock = mock(''a mock'') >>>> my_mock.stub!(:foo).and_return(:bar) >>>> >>>> AND: >>>> >>>> my_mock = mock("a mock", {:foo => :bar}) >>>> >>>> Scott >>>> >>>> _______________________________________________ >>>> rspec-users mailing list >>>> rspec-users at rubyforge.org >>>> http://rubyforge.org/mailman/listinfo/rspec-users >>>> >>> >>> Ok, then would you still insist that: >>> >>> This: >>> >>> http://gist.github.com/2372 >>> >>> Should produce this: >>> >>> # spec migration_spec.rb >>> .F >>> >>> 1) >>> Spec::Mocks::MockExpectationError in ''Migration should find the records'' >>> Mock ''MyModel Class'' received unexpected message :count with (no args) >>> ./migration.rb:14:in `run'' >>> ./migration_spec.rb:19: >>> >>> Finished in 0.009435 seconds >>> >>> -------------------- >>> >>> And like I said earlier, this code passes both examples with FlexMock( >>> if you simply replace mock with flexmock and uncomment the code in >>> spec_helper, of course you need the flexmock gem) >> >> I can''t speak for why it''s passing in Flexmock, but I can explain why >> it''s failing in rspec. >> >> RSpec clears out all stub methods and message expectations at the end >> of each example. In this case, the stub on count is defined in a >> before(:all) block, which is only executed once, before all the >> examples are run (perhaps before(:any) would be a more clear >> expression of this?). After the first example is executed, that stub >> goes away. So when the mock receives the :count message in the second >> example, it''s not expecting it (which is exactly what it''s telling >> you). If you run the second example by itself (spec migration_spec.rb >> -e "should find the records") it will pass. >> >> You can solve the immediate problem by removing the stubs from the >> initial declaration of the MyModel constant and moving them to a >> before(:each) block so they get set before each example. >> >> Another option is to set :null_object => true. That will tell the mock >> to ignore unexpected messages, however the stub on find might still >> need to move to before(:each) because it returns something. >> >> Also - this code creates instance variables that get used across >> examples. If something happens in the first example to change the >> state of @record, you''re going to get the same object in the second >> example and it becomes a challenge to understand what''s happening when >> there are failures in the second example. >> >> I don''t use before(:all) blocks this way for exactly this reason. They >> are run only once, and can cause a lot of confusion because they leak >> state across examples. The way I usually go about something like this >> is to create a simple empty class: >> >> class MyModel; end >> >> And then set expectations on it before(:each) example. >> >> You can get the gist of what I''m talking about here: >> http://gist.github.com/2438 - I''ve got two different approaches in two >> separate commits, so grab the repo to see both > > Or you *could* just look at them on line! > > https://gist.github.com/2438/040f26916032ad864ba51d0d733e16056c77be42 > https://gist.github.com/2438/0ee4fcaebbafdbdab77dffd5228a9aae92f17191 > > > >> (this is my first time >> checking out gist - wow!). >> >> HTH, >> David >> > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
David Chelimsky
2008-Jul-25 13:40 UTC
[rspec-users] Problems with mock assigned to a constant
> On Fri, Jul 25, 2008 at 8:15 AM, David Chelimsky <dchelimsky at gmail.com> wrote: >> On Fri, Jul 25, 2008 at 7:57 AM, David Chelimsky <dchelimsky at gmail.com> wrote: >>> On Fri, Jul 25, 2008 at 12:34 AM, Matt Lins <mattlins at gmail.com> wrote: >>>> On Fri, Jul 25, 2008 at 12:25 AM, Scott Taylor >>>> <mailing_lists at railsnewbie.com> wrote: >>>>> >>>>> On Jul 25, 2008, at 1:15 AM, Matt Lins wrote: >>>>> >>>>>> On Thu, Jul 24, 2008 at 11:47 PM, Scott Taylor >>>>>> <mailing_lists at railsnewbie.com> wrote: >>>>>>> >>>>>>> On Jul 25, 2008, at 12:32 AM, Matt Lins wrote: >>>>>>> >>>>>>>> I suppose the way I''m defining the stubs, differs from what Dave is >>>>>>>> doing in his example. >>>>>>>> >>>>>>>> I assumed that: >>>>>>>> >>>>>>>> MyModel = mock(''MyModel Class'', :count => 1) >>>>>>>> >>>>>>>> was the same as: >>>>>>>> >>>>>>>> MyModel.stub!(:count).and_return(1) >>>>>>> >>>>>>> Nope. Not even close. Here''s an equivalent of the first form: >>>>>>> >>>>>>> Object.send :remove_const, :MyModel >>>>>>> MyModel = <a mock object> >>>>>>> >>>>>>> and here''s the second form: >>>>>>> >>>>>>> MyModel.instance_eval do >>>>>>> def count >>>>>>> 1 >>>>>>> end >>>>>>> end >>>>>>> >>>>>>> (or:) >>>>>>> >>>>>>> MyModel.class_eval do >>>>>>> class << self >>>>>>> def count; 1; end >>>>>>> end >>>>>>> end >>>>>>> >>>>>>> Scott >>>>>>> >>>>>>> >>>>>> >>>>>> But the stubs are defined the same way in both occurrences, no? >>>>>> >>>>>> MyModel = mock(''MyModel Class'', :count => 1) >>>>>> >>>>>> By passing {:count => 1} to +stubs_and_options+ I should have defined >>>>>> stubs on the mock object. I''m using it as a shortcut for this: >>>>>> >>>>>> MyModel = mock(''MyModel Class'') >>>>>> MyModel.stub!(:count).and_return(1) >>>>>> >>>>>> If those example aren''t doing the exact same thing I guess I''m a >>>>>> little baffled (or maybe just need to go to sleep). >>>>> >>>>> The first one is redefining the constant ''MyModel''. The second one is just >>>>> redefining a class method (the constant isn''t changing - it''s remaining >>>>> whatever it was before - say, a class) >>>>> >>>>>> >>>>>> >>>>>>>> >>>>>>>> >>>>>>>> But, I''m starting to think they are not. I haven''t looked at the >>>>>>>> rSpec internals to verify, other than the parameter name: >>>>>>>> >>>>>>>> stubs_and_options+ lets you assign options and stub values >>>>>>>> at the same time. The only option available is :null_object. >>>>>>>> Anything else is treated as a stub value. >>>>>>>> >>>>>>>> So, is this problem? >>>>>>> >>>>>>> Yeah - so here are two related, but not equivalent ideas: mock objects, >>>>>>> and >>>>>>> stubs. A stub is just a faked out method - it can exist on a mock object >>>>>>> (a >>>>>>> completely fake object), or on a partial mock (i.e. a real object, with a >>>>>>> method faked out). mock(''My mock") is a mock object, >>>>>>> MyRealObject.stub!(:foo) is a real object with the method foo faked out. >>>>>>> >>>>>>> What is the difference between a mock object and a fake object? A mock >>>>>>> object will complain (read: raise an error) any time it receives a >>>>>>> message >>>>>>> which it doesn''t understand (i.e. one which hasn''t been explicitly >>>>>>> stubbed). >>>>>>> A real object will work as usual. (A null object mock is a special type >>>>>>> of >>>>>>> mock - one which never complains. For now, you shouldn''t worry about >>>>>>> it). >>>>>>> >>>>>> >>>>>> Ok, I get what you saying, but as I understand it I am explicitly >>>>>> stubbing out the methods on the _mock_ object and it''s still >>>>>> complaining. If +stubs_and_options+ isn''t stubbing, then what is it >>>>>> doing? >>>>> >>>>> That''s right - the hash to the mock method is a shorthand. So these two are >>>>> equivalent: >>>>> >>>>> my_mock = mock(''a mock'') >>>>> my_mock.stub!(:foo).and_return(:bar) >>>>> >>>>> AND: >>>>> >>>>> my_mock = mock("a mock", {:foo => :bar}) >>>>> >>>>> Scott >>>>> >>>>> _______________________________________________ >>>>> rspec-users mailing list >>>>> rspec-users at rubyforge.org >>>>> http://rubyforge.org/mailman/listinfo/rspec-users >>>>> >>>> >>>> Ok, then would you still insist that: >>>> >>>> This: >>>> >>>> http://gist.github.com/2372 >>>> >>>> Should produce this: >>>> >>>> # spec migration_spec.rb >>>> .F >>>> >>>> 1) >>>> Spec::Mocks::MockExpectationError in ''Migration should find the records'' >>>> Mock ''MyModel Class'' received unexpected message :count with (no args) >>>> ./migration.rb:14:in `run'' >>>> ./migration_spec.rb:19: >>>> >>>> Finished in 0.009435 seconds >>>> >>>> -------------------- >>>> >>>> And like I said earlier, this code passes both examples with FlexMock( >>>> if you simply replace mock with flexmock and uncomment the code in >>>> spec_helper, of course you need the flexmock gem) >>> >>> I can''t speak for why it''s passing in Flexmock, but I can explain why >>> it''s failing in rspec. >>> >>> RSpec clears out all stub methods and message expectations at the end >>> of each example. In this case, the stub on count is defined in a >>> before(:all) block, which is only executed once, before all the >>> examples are run (perhaps before(:any) would be a more clear >>> expression of this?). After the first example is executed, that stub >>> goes away. So when the mock receives the :count message in the second >>> example, it''s not expecting it (which is exactly what it''s telling >>> you). If you run the second example by itself (spec migration_spec.rb >>> -e "should find the records") it will pass. >>> >>> You can solve the immediate problem by removing the stubs from the >>> initial declaration of the MyModel constant and moving them to a >>> before(:each) block so they get set before each example. >>> >>> Another option is to set :null_object => true. That will tell the mock >>> to ignore unexpected messages, however the stub on find might still >>> need to move to before(:each) because it returns something. >>> >>> Also - this code creates instance variables that get used across >>> examples. If something happens in the first example to change the >>> state of @record, you''re going to get the same object in the second >>> example and it becomes a challenge to understand what''s happening when >>> there are failures in the second example. >>> >>> I don''t use before(:all) blocks this way for exactly this reason. They >>> are run only once, and can cause a lot of confusion because they leak >>> state across examples. The way I usually go about something like this >>> is to create a simple empty class: >>> >>> class MyModel; end >>> >>> And then set expectations on it before(:each) example. >>> >>> You can get the gist of what I''m talking about here: >>> http://gist.github.com/2438 - I''ve got two different approaches in two >>> separate commits, so grab the repo to see both >> >> Or you *could* just look at them on line! >> >> https://gist.github.com/2438/040f26916032ad864ba51d0d733e16056c77be42 >> https://gist.github.com/2438/0ee4fcaebbafdbdab77dffd5228a9aae92f17191 >> >> >> >>> (this is my first time >>> checking out gist - wow!). >>> >>> HTH, >>> DavidOn Fri, Jul 25, 2008 at 8:30 AM, Matt Lins <mattlins at gmail.com> wrote:> Yes, gist is great! > > Thank you very much for taking the time to look at this. I like your > suggestions very much and will use them. At this point I''m just > messing around, but I don''t understand why this doesn''t work. > > One more bad implementation if you have time:I don''t really have much - packing for a week''s holiday.> http://gist.github.com/2372 > > I''m removing the constant after each spec runs and redefining it > before each runs. The second spec still doesn''t pass though, even > though the constant is defined before each spec.Is is the same failure?> Again, I realize this is horrible, but it should still work, no?
On Fri, Jul 25, 2008 at 8:40 AM, David Chelimsky <dchelimsky at gmail.com> wrote:>> On Fri, Jul 25, 2008 at 8:15 AM, David Chelimsky <dchelimsky at gmail.com> wrote: >>> On Fri, Jul 25, 2008 at 7:57 AM, David Chelimsky <dchelimsky at gmail.com> wrote: >>>> On Fri, Jul 25, 2008 at 12:34 AM, Matt Lins <mattlins at gmail.com> wrote: >>>>> On Fri, Jul 25, 2008 at 12:25 AM, Scott Taylor >>>>> <mailing_lists at railsnewbie.com> wrote: >>>>>> >>>>>> On Jul 25, 2008, at 1:15 AM, Matt Lins wrote: >>>>>> >>>>>>> On Thu, Jul 24, 2008 at 11:47 PM, Scott Taylor >>>>>>> <mailing_lists at railsnewbie.com> wrote: >>>>>>>> >>>>>>>> On Jul 25, 2008, at 12:32 AM, Matt Lins wrote: >>>>>>>> >>>>>>>>> I suppose the way I''m defining the stubs, differs from what Dave is >>>>>>>>> doing in his example. >>>>>>>>> >>>>>>>>> I assumed that: >>>>>>>>> >>>>>>>>> MyModel = mock(''MyModel Class'', :count => 1) >>>>>>>>> >>>>>>>>> was the same as: >>>>>>>>> >>>>>>>>> MyModel.stub!(:count).and_return(1) >>>>>>>> >>>>>>>> Nope. Not even close. Here''s an equivalent of the first form: >>>>>>>> >>>>>>>> Object.send :remove_const, :MyModel >>>>>>>> MyModel = <a mock object> >>>>>>>> >>>>>>>> and here''s the second form: >>>>>>>> >>>>>>>> MyModel.instance_eval do >>>>>>>> def count >>>>>>>> 1 >>>>>>>> end >>>>>>>> end >>>>>>>> >>>>>>>> (or:) >>>>>>>> >>>>>>>> MyModel.class_eval do >>>>>>>> class << self >>>>>>>> def count; 1; end >>>>>>>> end >>>>>>>> end >>>>>>>> >>>>>>>> Scott >>>>>>>> >>>>>>>> >>>>>>> >>>>>>> But the stubs are defined the same way in both occurrences, no? >>>>>>> >>>>>>> MyModel = mock(''MyModel Class'', :count => 1) >>>>>>> >>>>>>> By passing {:count => 1} to +stubs_and_options+ I should have defined >>>>>>> stubs on the mock object. I''m using it as a shortcut for this: >>>>>>> >>>>>>> MyModel = mock(''MyModel Class'') >>>>>>> MyModel.stub!(:count).and_return(1) >>>>>>> >>>>>>> If those example aren''t doing the exact same thing I guess I''m a >>>>>>> little baffled (or maybe just need to go to sleep). >>>>>> >>>>>> The first one is redefining the constant ''MyModel''. The second one is just >>>>>> redefining a class method (the constant isn''t changing - it''s remaining >>>>>> whatever it was before - say, a class) >>>>>> >>>>>>> >>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>>> But, I''m starting to think they are not. I haven''t looked at the >>>>>>>>> rSpec internals to verify, other than the parameter name: >>>>>>>>> >>>>>>>>> stubs_and_options+ lets you assign options and stub values >>>>>>>>> at the same time. The only option available is :null_object. >>>>>>>>> Anything else is treated as a stub value. >>>>>>>>> >>>>>>>>> So, is this problem? >>>>>>>> >>>>>>>> Yeah - so here are two related, but not equivalent ideas: mock objects, >>>>>>>> and >>>>>>>> stubs. A stub is just a faked out method - it can exist on a mock object >>>>>>>> (a >>>>>>>> completely fake object), or on a partial mock (i.e. a real object, with a >>>>>>>> method faked out). mock(''My mock") is a mock object, >>>>>>>> MyRealObject.stub!(:foo) is a real object with the method foo faked out. >>>>>>>> >>>>>>>> What is the difference between a mock object and a fake object? A mock >>>>>>>> object will complain (read: raise an error) any time it receives a >>>>>>>> message >>>>>>>> which it doesn''t understand (i.e. one which hasn''t been explicitly >>>>>>>> stubbed). >>>>>>>> A real object will work as usual. (A null object mock is a special type >>>>>>>> of >>>>>>>> mock - one which never complains. For now, you shouldn''t worry about >>>>>>>> it). >>>>>>>> >>>>>>> >>>>>>> Ok, I get what you saying, but as I understand it I am explicitly >>>>>>> stubbing out the methods on the _mock_ object and it''s still >>>>>>> complaining. If +stubs_and_options+ isn''t stubbing, then what is it >>>>>>> doing? >>>>>> >>>>>> That''s right - the hash to the mock method is a shorthand. So these two are >>>>>> equivalent: >>>>>> >>>>>> my_mock = mock(''a mock'') >>>>>> my_mock.stub!(:foo).and_return(:bar) >>>>>> >>>>>> AND: >>>>>> >>>>>> my_mock = mock("a mock", {:foo => :bar}) >>>>>> >>>>>> Scott >>>>>> >>>>>> _______________________________________________ >>>>>> rspec-users mailing list >>>>>> rspec-users at rubyforge.org >>>>>> http://rubyforge.org/mailman/listinfo/rspec-users >>>>>> >>>>> >>>>> Ok, then would you still insist that: >>>>> >>>>> This: >>>>> >>>>> http://gist.github.com/2372 >>>>> >>>>> Should produce this: >>>>> >>>>> # spec migration_spec.rb >>>>> .F >>>>> >>>>> 1) >>>>> Spec::Mocks::MockExpectationError in ''Migration should find the records'' >>>>> Mock ''MyModel Class'' received unexpected message :count with (no args) >>>>> ./migration.rb:14:in `run'' >>>>> ./migration_spec.rb:19: >>>>> >>>>> Finished in 0.009435 seconds >>>>> >>>>> -------------------- >>>>> >>>>> And like I said earlier, this code passes both examples with FlexMock( >>>>> if you simply replace mock with flexmock and uncomment the code in >>>>> spec_helper, of course you need the flexmock gem) >>>> >>>> I can''t speak for why it''s passing in Flexmock, but I can explain why >>>> it''s failing in rspec. >>>> >>>> RSpec clears out all stub methods and message expectations at the end >>>> of each example. In this case, the stub on count is defined in a >>>> before(:all) block, which is only executed once, before all the >>>> examples are run (perhaps before(:any) would be a more clear >>>> expression of this?). After the first example is executed, that stub >>>> goes away. So when the mock receives the :count message in the second >>>> example, it''s not expecting it (which is exactly what it''s telling >>>> you). If you run the second example by itself (spec migration_spec.rb >>>> -e "should find the records") it will pass. >>>> >>>> You can solve the immediate problem by removing the stubs from the >>>> initial declaration of the MyModel constant and moving them to a >>>> before(:each) block so they get set before each example. >>>> >>>> Another option is to set :null_object => true. That will tell the mock >>>> to ignore unexpected messages, however the stub on find might still >>>> need to move to before(:each) because it returns something. >>>> >>>> Also - this code creates instance variables that get used across >>>> examples. If something happens in the first example to change the >>>> state of @record, you''re going to get the same object in the second >>>> example and it becomes a challenge to understand what''s happening when >>>> there are failures in the second example. >>>> >>>> I don''t use before(:all) blocks this way for exactly this reason. They >>>> are run only once, and can cause a lot of confusion because they leak >>>> state across examples. The way I usually go about something like this >>>> is to create a simple empty class: >>>> >>>> class MyModel; end >>>> >>>> And then set expectations on it before(:each) example. >>>> >>>> You can get the gist of what I''m talking about here: >>>> http://gist.github.com/2438 - I''ve got two different approaches in two >>>> separate commits, so grab the repo to see both >>> >>> Or you *could* just look at them on line! >>> >>> https://gist.github.com/2438/040f26916032ad864ba51d0d733e16056c77be42 >>> https://gist.github.com/2438/0ee4fcaebbafdbdab77dffd5228a9aae92f17191 >>> >>> >>> >>>> (this is my first time >>>> checking out gist - wow!). >>>> >>>> HTH, >>>> David > > On Fri, Jul 25, 2008 at 8:30 AM, Matt Lins <mattlins at gmail.com> wrote: >> Yes, gist is great! >> >> Thank you very much for taking the time to look at this. I like your >> suggestions very much and will use them. At this point I''m just >> messing around, but I don''t understand why this doesn''t work. >> >> One more bad implementation if you have time: > > I don''t really have much - packing for a week''s holiday.Ok, thanks anyway. Enjoy your holiday. :)> >> http://gist.github.com/2372 >> >> I''m removing the constant after each spec runs and redefining it >> before each runs. The second spec still doesn''t pass though, even >> though the constant is defined before each spec. > > Is is the same failure?Yes.> >> Again, I realize this is horrible, but it should still work, no? > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
David Chelimsky
2008-Jul-25 13:49 UTC
[rspec-users] Problems with mock assigned to a constant
On Fri, Jul 25, 2008 at 8:44 AM, Matt Lins <mattlins at gmail.com> wrote:> On Fri, Jul 25, 2008 at 8:40 AM, David Chelimsky <dchelimsky at gmail.com> wrote: >>> On Fri, Jul 25, 2008 at 8:15 AM, David Chelimsky <dchelimsky at gmail.com> wrote: >>>> On Fri, Jul 25, 2008 at 7:57 AM, David Chelimsky <dchelimsky at gmail.com> wrote: >>>>> On Fri, Jul 25, 2008 at 12:34 AM, Matt Lins <mattlins at gmail.com> wrote: >>>>>> On Fri, Jul 25, 2008 at 12:25 AM, Scott Taylor >>>>>> <mailing_lists at railsnewbie.com> wrote: >>>>>>> >>>>>>> On Jul 25, 2008, at 1:15 AM, Matt Lins wrote: >>>>>>> >>>>>>>> On Thu, Jul 24, 2008 at 11:47 PM, Scott Taylor >>>>>>>> <mailing_lists at railsnewbie.com> wrote: >>>>>>>>> >>>>>>>>> On Jul 25, 2008, at 12:32 AM, Matt Lins wrote: >>>>>>>>> >>>>>>>>>> I suppose the way I''m defining the stubs, differs from what Dave is >>>>>>>>>> doing in his example. >>>>>>>>>> >>>>>>>>>> I assumed that: >>>>>>>>>> >>>>>>>>>> MyModel = mock(''MyModel Class'', :count => 1) >>>>>>>>>> >>>>>>>>>> was the same as: >>>>>>>>>> >>>>>>>>>> MyModel.stub!(:count).and_return(1) >>>>>>>>> >>>>>>>>> Nope. Not even close. Here''s an equivalent of the first form: >>>>>>>>> >>>>>>>>> Object.send :remove_const, :MyModel >>>>>>>>> MyModel = <a mock object> >>>>>>>>> >>>>>>>>> and here''s the second form: >>>>>>>>> >>>>>>>>> MyModel.instance_eval do >>>>>>>>> def count >>>>>>>>> 1 >>>>>>>>> end >>>>>>>>> end >>>>>>>>> >>>>>>>>> (or:) >>>>>>>>> >>>>>>>>> MyModel.class_eval do >>>>>>>>> class << self >>>>>>>>> def count; 1; end >>>>>>>>> end >>>>>>>>> end >>>>>>>>> >>>>>>>>> Scott >>>>>>>>> >>>>>>>>> >>>>>>>> >>>>>>>> But the stubs are defined the same way in both occurrences, no? >>>>>>>> >>>>>>>> MyModel = mock(''MyModel Class'', :count => 1) >>>>>>>> >>>>>>>> By passing {:count => 1} to +stubs_and_options+ I should have defined >>>>>>>> stubs on the mock object. I''m using it as a shortcut for this: >>>>>>>> >>>>>>>> MyModel = mock(''MyModel Class'') >>>>>>>> MyModel.stub!(:count).and_return(1) >>>>>>>> >>>>>>>> If those example aren''t doing the exact same thing I guess I''m a >>>>>>>> little baffled (or maybe just need to go to sleep). >>>>>>> >>>>>>> The first one is redefining the constant ''MyModel''. The second one is just >>>>>>> redefining a class method (the constant isn''t changing - it''s remaining >>>>>>> whatever it was before - say, a class) >>>>>>> >>>>>>>> >>>>>>>> >>>>>>>>>> >>>>>>>>>> >>>>>>>>>> But, I''m starting to think they are not. I haven''t looked at the >>>>>>>>>> rSpec internals to verify, other than the parameter name: >>>>>>>>>> >>>>>>>>>> stubs_and_options+ lets you assign options and stub values >>>>>>>>>> at the same time. The only option available is :null_object. >>>>>>>>>> Anything else is treated as a stub value. >>>>>>>>>> >>>>>>>>>> So, is this problem? >>>>>>>>> >>>>>>>>> Yeah - so here are two related, but not equivalent ideas: mock objects, >>>>>>>>> and >>>>>>>>> stubs. A stub is just a faked out method - it can exist on a mock object >>>>>>>>> (a >>>>>>>>> completely fake object), or on a partial mock (i.e. a real object, with a >>>>>>>>> method faked out). mock(''My mock") is a mock object, >>>>>>>>> MyRealObject.stub!(:foo) is a real object with the method foo faked out. >>>>>>>>> >>>>>>>>> What is the difference between a mock object and a fake object? A mock >>>>>>>>> object will complain (read: raise an error) any time it receives a >>>>>>>>> message >>>>>>>>> which it doesn''t understand (i.e. one which hasn''t been explicitly >>>>>>>>> stubbed). >>>>>>>>> A real object will work as usual. (A null object mock is a special type >>>>>>>>> of >>>>>>>>> mock - one which never complains. For now, you shouldn''t worry about >>>>>>>>> it). >>>>>>>>> >>>>>>>> >>>>>>>> Ok, I get what you saying, but as I understand it I am explicitly >>>>>>>> stubbing out the methods on the _mock_ object and it''s still >>>>>>>> complaining. If +stubs_and_options+ isn''t stubbing, then what is it >>>>>>>> doing? >>>>>>> >>>>>>> That''s right - the hash to the mock method is a shorthand. So these two are >>>>>>> equivalent: >>>>>>> >>>>>>> my_mock = mock(''a mock'') >>>>>>> my_mock.stub!(:foo).and_return(:bar) >>>>>>> >>>>>>> AND: >>>>>>> >>>>>>> my_mock = mock("a mock", {:foo => :bar}) >>>>>>> >>>>>>> Scott >>>>>>> >>>>>>> _______________________________________________ >>>>>>> rspec-users mailing list >>>>>>> rspec-users at rubyforge.org >>>>>>> http://rubyforge.org/mailman/listinfo/rspec-users >>>>>>> >>>>>> >>>>>> Ok, then would you still insist that: >>>>>> >>>>>> This: >>>>>> >>>>>> http://gist.github.com/2372 >>>>>> >>>>>> Should produce this: >>>>>> >>>>>> # spec migration_spec.rb >>>>>> .F >>>>>> >>>>>> 1) >>>>>> Spec::Mocks::MockExpectationError in ''Migration should find the records'' >>>>>> Mock ''MyModel Class'' received unexpected message :count with (no args) >>>>>> ./migration.rb:14:in `run'' >>>>>> ./migration_spec.rb:19: >>>>>> >>>>>> Finished in 0.009435 seconds >>>>>> >>>>>> -------------------- >>>>>> >>>>>> And like I said earlier, this code passes both examples with FlexMock( >>>>>> if you simply replace mock with flexmock and uncomment the code in >>>>>> spec_helper, of course you need the flexmock gem) >>>>> >>>>> I can''t speak for why it''s passing in Flexmock, but I can explain why >>>>> it''s failing in rspec. >>>>> >>>>> RSpec clears out all stub methods and message expectations at the end >>>>> of each example. In this case, the stub on count is defined in a >>>>> before(:all) block, which is only executed once, before all the >>>>> examples are run (perhaps before(:any) would be a more clear >>>>> expression of this?). After the first example is executed, that stub >>>>> goes away. So when the mock receives the :count message in the second >>>>> example, it''s not expecting it (which is exactly what it''s telling >>>>> you). If you run the second example by itself (spec migration_spec.rb >>>>> -e "should find the records") it will pass. >>>>> >>>>> You can solve the immediate problem by removing the stubs from the >>>>> initial declaration of the MyModel constant and moving them to a >>>>> before(:each) block so they get set before each example. >>>>> >>>>> Another option is to set :null_object => true. That will tell the mock >>>>> to ignore unexpected messages, however the stub on find might still >>>>> need to move to before(:each) because it returns something. >>>>> >>>>> Also - this code creates instance variables that get used across >>>>> examples. If something happens in the first example to change the >>>>> state of @record, you''re going to get the same object in the second >>>>> example and it becomes a challenge to understand what''s happening when >>>>> there are failures in the second example. >>>>> >>>>> I don''t use before(:all) blocks this way for exactly this reason. They >>>>> are run only once, and can cause a lot of confusion because they leak >>>>> state across examples. The way I usually go about something like this >>>>> is to create a simple empty class: >>>>> >>>>> class MyModel; end >>>>> >>>>> And then set expectations on it before(:each) example. >>>>> >>>>> You can get the gist of what I''m talking about here: >>>>> http://gist.github.com/2438 - I''ve got two different approaches in two >>>>> separate commits, so grab the repo to see both >>>> >>>> Or you *could* just look at them on line! >>>> >>>> https://gist.github.com/2438/040f26916032ad864ba51d0d733e16056c77be42 >>>> https://gist.github.com/2438/0ee4fcaebbafdbdab77dffd5228a9aae92f17191 >>>> >>>> >>>> >>>>> (this is my first time >>>>> checking out gist - wow!). >>>>> >>>>> HTH, >>>>> David >> >> On Fri, Jul 25, 2008 at 8:30 AM, Matt Lins <mattlins at gmail.com> wrote: >>> Yes, gist is great! >>> >>> Thank you very much for taking the time to look at this. I like your >>> suggestions very much and will use them. At this point I''m just >>> messing around, but I don''t understand why this doesn''t work. >>> >>> One more bad implementation if you have time: >> >> I don''t really have much - packing for a week''s holiday. > > Ok, thanks anyway. Enjoy your holiday. :) > >> >>> http://gist.github.com/2372 >>> >>> I''m removing the constant after each spec runs and redefining it >>> before each runs. The second spec still doesn''t pass though, even >>> though the constant is defined before each spec. >> >> Is is the same failure? > > Yes.What about when you run them each individually? spec migration_spec.rb - e ''should get a count of the records'' spec migration_spec.rb - e ''should find the records''> >> >>> Again, I realize this is horrible, but it should still work, no? >> _______________________________________________ >> rspec-users mailing list >> rspec-users at rubyforge.org >> http://rubyforge.org/mailman/listinfo/rspec-users >> > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
David Chelimsky
2008-Jul-25 13:53 UTC
[rspec-users] Problems with mock assigned to a constant
On Fri, Jul 25, 2008 at 8:49 AM, David Chelimsky <dchelimsky at gmail.com> wrote:> On Fri, Jul 25, 2008 at 8:44 AM, Matt Lins <mattlins at gmail.com> wrote: >> On Fri, Jul 25, 2008 at 8:40 AM, David Chelimsky <dchelimsky at gmail.com> wrote: >>>> On Fri, Jul 25, 2008 at 8:15 AM, David Chelimsky <dchelimsky at gmail.com> wrote: >>>>> On Fri, Jul 25, 2008 at 7:57 AM, David Chelimsky <dchelimsky at gmail.com> wrote: >>>>>> On Fri, Jul 25, 2008 at 12:34 AM, Matt Lins <mattlins at gmail.com> wrote: >>>>>>> On Fri, Jul 25, 2008 at 12:25 AM, Scott Taylor >>>>>>> <mailing_lists at railsnewbie.com> wrote: >>>>>>>> >>>>>>>> On Jul 25, 2008, at 1:15 AM, Matt Lins wrote: >>>>>>>> >>>>>>>>> On Thu, Jul 24, 2008 at 11:47 PM, Scott Taylor >>>>>>>>> <mailing_lists at railsnewbie.com> wrote: >>>>>>>>>> >>>>>>>>>> On Jul 25, 2008, at 12:32 AM, Matt Lins wrote: >>>>>>>>>> >>>>>>>>>>> I suppose the way I''m defining the stubs, differs from what Dave is >>>>>>>>>>> doing in his example. >>>>>>>>>>> >>>>>>>>>>> I assumed that: >>>>>>>>>>> >>>>>>>>>>> MyModel = mock(''MyModel Class'', :count => 1) >>>>>>>>>>> >>>>>>>>>>> was the same as: >>>>>>>>>>> >>>>>>>>>>> MyModel.stub!(:count).and_return(1) >>>>>>>>>> >>>>>>>>>> Nope. Not even close. Here''s an equivalent of the first form: >>>>>>>>>> >>>>>>>>>> Object.send :remove_const, :MyModel >>>>>>>>>> MyModel = <a mock object> >>>>>>>>>> >>>>>>>>>> and here''s the second form: >>>>>>>>>> >>>>>>>>>> MyModel.instance_eval do >>>>>>>>>> def count >>>>>>>>>> 1 >>>>>>>>>> end >>>>>>>>>> end >>>>>>>>>> >>>>>>>>>> (or:) >>>>>>>>>> >>>>>>>>>> MyModel.class_eval do >>>>>>>>>> class << self >>>>>>>>>> def count; 1; end >>>>>>>>>> end >>>>>>>>>> end >>>>>>>>>> >>>>>>>>>> Scott >>>>>>>>>> >>>>>>>>>> >>>>>>>>> >>>>>>>>> But the stubs are defined the same way in both occurrences, no? >>>>>>>>> >>>>>>>>> MyModel = mock(''MyModel Class'', :count => 1) >>>>>>>>> >>>>>>>>> By passing {:count => 1} to +stubs_and_options+ I should have defined >>>>>>>>> stubs on the mock object. I''m using it as a shortcut for this: >>>>>>>>> >>>>>>>>> MyModel = mock(''MyModel Class'') >>>>>>>>> MyModel.stub!(:count).and_return(1) >>>>>>>>> >>>>>>>>> If those example aren''t doing the exact same thing I guess I''m a >>>>>>>>> little baffled (or maybe just need to go to sleep). >>>>>>>> >>>>>>>> The first one is redefining the constant ''MyModel''. The second one is just >>>>>>>> redefining a class method (the constant isn''t changing - it''s remaining >>>>>>>> whatever it was before - say, a class) >>>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>>>>> >>>>>>>>>>> >>>>>>>>>>> But, I''m starting to think they are not. I haven''t looked at the >>>>>>>>>>> rSpec internals to verify, other than the parameter name: >>>>>>>>>>> >>>>>>>>>>> stubs_and_options+ lets you assign options and stub values >>>>>>>>>>> at the same time. The only option available is :null_object. >>>>>>>>>>> Anything else is treated as a stub value. >>>>>>>>>>> >>>>>>>>>>> So, is this problem? >>>>>>>>>> >>>>>>>>>> Yeah - so here are two related, but not equivalent ideas: mock objects, >>>>>>>>>> and >>>>>>>>>> stubs. A stub is just a faked out method - it can exist on a mock object >>>>>>>>>> (a >>>>>>>>>> completely fake object), or on a partial mock (i.e. a real object, with a >>>>>>>>>> method faked out). mock(''My mock") is a mock object, >>>>>>>>>> MyRealObject.stub!(:foo) is a real object with the method foo faked out. >>>>>>>>>> >>>>>>>>>> What is the difference between a mock object and a fake object? A mock >>>>>>>>>> object will complain (read: raise an error) any time it receives a >>>>>>>>>> message >>>>>>>>>> which it doesn''t understand (i.e. one which hasn''t been explicitly >>>>>>>>>> stubbed). >>>>>>>>>> A real object will work as usual. (A null object mock is a special type >>>>>>>>>> of >>>>>>>>>> mock - one which never complains. For now, you shouldn''t worry about >>>>>>>>>> it). >>>>>>>>>> >>>>>>>>> >>>>>>>>> Ok, I get what you saying, but as I understand it I am explicitly >>>>>>>>> stubbing out the methods on the _mock_ object and it''s still >>>>>>>>> complaining. If +stubs_and_options+ isn''t stubbing, then what is it >>>>>>>>> doing? >>>>>>>> >>>>>>>> That''s right - the hash to the mock method is a shorthand. So these two are >>>>>>>> equivalent: >>>>>>>> >>>>>>>> my_mock = mock(''a mock'') >>>>>>>> my_mock.stub!(:foo).and_return(:bar) >>>>>>>> >>>>>>>> AND: >>>>>>>> >>>>>>>> my_mock = mock("a mock", {:foo => :bar}) >>>>>>>> >>>>>>>> Scott >>>>>>>> >>>>>>>> _______________________________________________ >>>>>>>> rspec-users mailing list >>>>>>>> rspec-users at rubyforge.org >>>>>>>> http://rubyforge.org/mailman/listinfo/rspec-users >>>>>>>> >>>>>>> >>>>>>> Ok, then would you still insist that: >>>>>>> >>>>>>> This: >>>>>>> >>>>>>> http://gist.github.com/2372 >>>>>>> >>>>>>> Should produce this: >>>>>>> >>>>>>> # spec migration_spec.rb >>>>>>> .F >>>>>>> >>>>>>> 1) >>>>>>> Spec::Mocks::MockExpectationError in ''Migration should find the records'' >>>>>>> Mock ''MyModel Class'' received unexpected message :count with (no args) >>>>>>> ./migration.rb:14:in `run'' >>>>>>> ./migration_spec.rb:19: >>>>>>> >>>>>>> Finished in 0.009435 seconds >>>>>>> >>>>>>> -------------------- >>>>>>> >>>>>>> And like I said earlier, this code passes both examples with FlexMock( >>>>>>> if you simply replace mock with flexmock and uncomment the code in >>>>>>> spec_helper, of course you need the flexmock gem) >>>>>> >>>>>> I can''t speak for why it''s passing in Flexmock, but I can explain why >>>>>> it''s failing in rspec. >>>>>> >>>>>> RSpec clears out all stub methods and message expectations at the end >>>>>> of each example. In this case, the stub on count is defined in a >>>>>> before(:all) block, which is only executed once, before all the >>>>>> examples are run (perhaps before(:any) would be a more clear >>>>>> expression of this?). After the first example is executed, that stub >>>>>> goes away. So when the mock receives the :count message in the second >>>>>> example, it''s not expecting it (which is exactly what it''s telling >>>>>> you). If you run the second example by itself (spec migration_spec.rb >>>>>> -e "should find the records") it will pass. >>>>>> >>>>>> You can solve the immediate problem by removing the stubs from the >>>>>> initial declaration of the MyModel constant and moving them to a >>>>>> before(:each) block so they get set before each example. >>>>>> >>>>>> Another option is to set :null_object => true. That will tell the mock >>>>>> to ignore unexpected messages, however the stub on find might still >>>>>> need to move to before(:each) because it returns something. >>>>>> >>>>>> Also - this code creates instance variables that get used across >>>>>> examples. If something happens in the first example to change the >>>>>> state of @record, you''re going to get the same object in the second >>>>>> example and it becomes a challenge to understand what''s happening when >>>>>> there are failures in the second example. >>>>>> >>>>>> I don''t use before(:all) blocks this way for exactly this reason. They >>>>>> are run only once, and can cause a lot of confusion because they leak >>>>>> state across examples. The way I usually go about something like this >>>>>> is to create a simple empty class: >>>>>> >>>>>> class MyModel; end >>>>>> >>>>>> And then set expectations on it before(:each) example. >>>>>> >>>>>> You can get the gist of what I''m talking about here: >>>>>> http://gist.github.com/2438 - I''ve got two different approaches in two >>>>>> separate commits, so grab the repo to see both >>>>> >>>>> Or you *could* just look at them on line! >>>>> >>>>> https://gist.github.com/2438/040f26916032ad864ba51d0d733e16056c77be42 >>>>> https://gist.github.com/2438/0ee4fcaebbafdbdab77dffd5228a9aae92f17191 >>>>> >>>>> >>>>> >>>>>> (this is my first time >>>>>> checking out gist - wow!). >>>>>> >>>>>> HTH, >>>>>> David >>> >>> On Fri, Jul 25, 2008 at 8:30 AM, Matt Lins <mattlins at gmail.com> wrote: >>>> Yes, gist is great! >>>> >>>> Thank you very much for taking the time to look at this. I like your >>>> suggestions very much and will use them. At this point I''m just >>>> messing around, but I don''t understand why this doesn''t work. >>>> >>>> One more bad implementation if you have time: >>> >>> I don''t really have much - packing for a week''s holiday. >> >> Ok, thanks anyway. Enjoy your holiday. :) >> >>> >>>> http://gist.github.com/2372 >>>> >>>> I''m removing the constant after each spec runs and redefining it >>>> before each runs. The second spec still doesn''t pass though, even >>>> though the constant is defined before each spec. >>> >>> Is is the same failure? >> >> Yes. > > What about when you run them each individually? > > spec migration_spec.rb - e ''should get a count of the records'' > spec migration_spec.rb - e ''should find the records''I actually just did that myself and they both pass separately - so there is some state-leaking problem. I don''t really have the time to investigate this now, but this is beginning to feel a lot like "doctor, it hurts when I bang my head against the wall like this ...."> > >> >>> >>>> Again, I realize this is horrible, but it should still work, no? >>> _______________________________________________ >>> rspec-users mailing list >>> rspec-users at rubyforge.org >>> http://rubyforge.org/mailman/listinfo/rspec-users >>> >> _______________________________________________ >> rspec-users mailing list >> rspec-users at rubyforge.org >> http://rubyforge.org/mailman/listinfo/rspec-users >> >
Fair enough. Thanks. On Fri, Jul 25, 2008 at 8:53 AM, David Chelimsky <dchelimsky at gmail.com> wrote:> On Fri, Jul 25, 2008 at 8:49 AM, David Chelimsky <dchelimsky at gmail.com> wrote: >> On Fri, Jul 25, 2008 at 8:44 AM, Matt Lins <mattlins at gmail.com> wrote: >>> On Fri, Jul 25, 2008 at 8:40 AM, David Chelimsky <dchelimsky at gmail.com> wrote: >>>>> On Fri, Jul 25, 2008 at 8:15 AM, David Chelimsky <dchelimsky at gmail.com> wrote: >>>>>> On Fri, Jul 25, 2008 at 7:57 AM, David Chelimsky <dchelimsky at gmail.com> wrote: >>>>>>> On Fri, Jul 25, 2008 at 12:34 AM, Matt Lins <mattlins at gmail.com> wrote: >>>>>>>> On Fri, Jul 25, 2008 at 12:25 AM, Scott Taylor >>>>>>>> <mailing_lists at railsnewbie.com> wrote: >>>>>>>>> >>>>>>>>> On Jul 25, 2008, at 1:15 AM, Matt Lins wrote: >>>>>>>>> >>>>>>>>>> On Thu, Jul 24, 2008 at 11:47 PM, Scott Taylor >>>>>>>>>> <mailing_lists at railsnewbie.com> wrote: >>>>>>>>>>> >>>>>>>>>>> On Jul 25, 2008, at 12:32 AM, Matt Lins wrote: >>>>>>>>>>> >>>>>>>>>>>> I suppose the way I''m defining the stubs, differs from what Dave is >>>>>>>>>>>> doing in his example. >>>>>>>>>>>> >>>>>>>>>>>> I assumed that: >>>>>>>>>>>> >>>>>>>>>>>> MyModel = mock(''MyModel Class'', :count => 1) >>>>>>>>>>>> >>>>>>>>>>>> was the same as: >>>>>>>>>>>> >>>>>>>>>>>> MyModel.stub!(:count).and_return(1) >>>>>>>>>>> >>>>>>>>>>> Nope. Not even close. Here''s an equivalent of the first form: >>>>>>>>>>> >>>>>>>>>>> Object.send :remove_const, :MyModel >>>>>>>>>>> MyModel = <a mock object> >>>>>>>>>>> >>>>>>>>>>> and here''s the second form: >>>>>>>>>>> >>>>>>>>>>> MyModel.instance_eval do >>>>>>>>>>> def count >>>>>>>>>>> 1 >>>>>>>>>>> end >>>>>>>>>>> end >>>>>>>>>>> >>>>>>>>>>> (or:) >>>>>>>>>>> >>>>>>>>>>> MyModel.class_eval do >>>>>>>>>>> class << self >>>>>>>>>>> def count; 1; end >>>>>>>>>>> end >>>>>>>>>>> end >>>>>>>>>>> >>>>>>>>>>> Scott >>>>>>>>>>> >>>>>>>>>>> >>>>>>>>>> >>>>>>>>>> But the stubs are defined the same way in both occurrences, no? >>>>>>>>>> >>>>>>>>>> MyModel = mock(''MyModel Class'', :count => 1) >>>>>>>>>> >>>>>>>>>> By passing {:count => 1} to +stubs_and_options+ I should have defined >>>>>>>>>> stubs on the mock object. I''m using it as a shortcut for this: >>>>>>>>>> >>>>>>>>>> MyModel = mock(''MyModel Class'') >>>>>>>>>> MyModel.stub!(:count).and_return(1) >>>>>>>>>> >>>>>>>>>> If those example aren''t doing the exact same thing I guess I''m a >>>>>>>>>> little baffled (or maybe just need to go to sleep). >>>>>>>>> >>>>>>>>> The first one is redefining the constant ''MyModel''. The second one is just >>>>>>>>> redefining a class method (the constant isn''t changing - it''s remaining >>>>>>>>> whatever it was before - say, a class) >>>>>>>>> >>>>>>>>>> >>>>>>>>>> >>>>>>>>>>>> >>>>>>>>>>>> >>>>>>>>>>>> But, I''m starting to think they are not. I haven''t looked at the >>>>>>>>>>>> rSpec internals to verify, other than the parameter name: >>>>>>>>>>>> >>>>>>>>>>>> stubs_and_options+ lets you assign options and stub values >>>>>>>>>>>> at the same time. The only option available is :null_object. >>>>>>>>>>>> Anything else is treated as a stub value. >>>>>>>>>>>> >>>>>>>>>>>> So, is this problem? >>>>>>>>>>> >>>>>>>>>>> Yeah - so here are two related, but not equivalent ideas: mock objects, >>>>>>>>>>> and >>>>>>>>>>> stubs. A stub is just a faked out method - it can exist on a mock object >>>>>>>>>>> (a >>>>>>>>>>> completely fake object), or on a partial mock (i.e. a real object, with a >>>>>>>>>>> method faked out). mock(''My mock") is a mock object, >>>>>>>>>>> MyRealObject.stub!(:foo) is a real object with the method foo faked out. >>>>>>>>>>> >>>>>>>>>>> What is the difference between a mock object and a fake object? A mock >>>>>>>>>>> object will complain (read: raise an error) any time it receives a >>>>>>>>>>> message >>>>>>>>>>> which it doesn''t understand (i.e. one which hasn''t been explicitly >>>>>>>>>>> stubbed). >>>>>>>>>>> A real object will work as usual. (A null object mock is a special type >>>>>>>>>>> of >>>>>>>>>>> mock - one which never complains. For now, you shouldn''t worry about >>>>>>>>>>> it). >>>>>>>>>>> >>>>>>>>>> >>>>>>>>>> Ok, I get what you saying, but as I understand it I am explicitly >>>>>>>>>> stubbing out the methods on the _mock_ object and it''s still >>>>>>>>>> complaining. If +stubs_and_options+ isn''t stubbing, then what is it >>>>>>>>>> doing? >>>>>>>>> >>>>>>>>> That''s right - the hash to the mock method is a shorthand. So these two are >>>>>>>>> equivalent: >>>>>>>>> >>>>>>>>> my_mock = mock(''a mock'') >>>>>>>>> my_mock.stub!(:foo).and_return(:bar) >>>>>>>>> >>>>>>>>> AND: >>>>>>>>> >>>>>>>>> my_mock = mock("a mock", {:foo => :bar}) >>>>>>>>> >>>>>>>>> Scott >>>>>>>>> >>>>>>>>> _______________________________________________ >>>>>>>>> rspec-users mailing list >>>>>>>>> rspec-users at rubyforge.org >>>>>>>>> http://rubyforge.org/mailman/listinfo/rspec-users >>>>>>>>> >>>>>>>> >>>>>>>> Ok, then would you still insist that: >>>>>>>> >>>>>>>> This: >>>>>>>> >>>>>>>> http://gist.github.com/2372 >>>>>>>> >>>>>>>> Should produce this: >>>>>>>> >>>>>>>> # spec migration_spec.rb >>>>>>>> .F >>>>>>>> >>>>>>>> 1) >>>>>>>> Spec::Mocks::MockExpectationError in ''Migration should find the records'' >>>>>>>> Mock ''MyModel Class'' received unexpected message :count with (no args) >>>>>>>> ./migration.rb:14:in `run'' >>>>>>>> ./migration_spec.rb:19: >>>>>>>> >>>>>>>> Finished in 0.009435 seconds >>>>>>>> >>>>>>>> -------------------- >>>>>>>> >>>>>>>> And like I said earlier, this code passes both examples with FlexMock( >>>>>>>> if you simply replace mock with flexmock and uncomment the code in >>>>>>>> spec_helper, of course you need the flexmock gem) >>>>>>> >>>>>>> I can''t speak for why it''s passing in Flexmock, but I can explain why >>>>>>> it''s failing in rspec. >>>>>>> >>>>>>> RSpec clears out all stub methods and message expectations at the end >>>>>>> of each example. In this case, the stub on count is defined in a >>>>>>> before(:all) block, which is only executed once, before all the >>>>>>> examples are run (perhaps before(:any) would be a more clear >>>>>>> expression of this?). After the first example is executed, that stub >>>>>>> goes away. So when the mock receives the :count message in the second >>>>>>> example, it''s not expecting it (which is exactly what it''s telling >>>>>>> you). If you run the second example by itself (spec migration_spec.rb >>>>>>> -e "should find the records") it will pass. >>>>>>> >>>>>>> You can solve the immediate problem by removing the stubs from the >>>>>>> initial declaration of the MyModel constant and moving them to a >>>>>>> before(:each) block so they get set before each example. >>>>>>> >>>>>>> Another option is to set :null_object => true. That will tell the mock >>>>>>> to ignore unexpected messages, however the stub on find might still >>>>>>> need to move to before(:each) because it returns something. >>>>>>> >>>>>>> Also - this code creates instance variables that get used across >>>>>>> examples. If something happens in the first example to change the >>>>>>> state of @record, you''re going to get the same object in the second >>>>>>> example and it becomes a challenge to understand what''s happening when >>>>>>> there are failures in the second example. >>>>>>> >>>>>>> I don''t use before(:all) blocks this way for exactly this reason. They >>>>>>> are run only once, and can cause a lot of confusion because they leak >>>>>>> state across examples. The way I usually go about something like this >>>>>>> is to create a simple empty class: >>>>>>> >>>>>>> class MyModel; end >>>>>>> >>>>>>> And then set expectations on it before(:each) example. >>>>>>> >>>>>>> You can get the gist of what I''m talking about here: >>>>>>> http://gist.github.com/2438 - I''ve got two different approaches in two >>>>>>> separate commits, so grab the repo to see both >>>>>> >>>>>> Or you *could* just look at them on line! >>>>>> >>>>>> https://gist.github.com/2438/040f26916032ad864ba51d0d733e16056c77be42 >>>>>> https://gist.github.com/2438/0ee4fcaebbafdbdab77dffd5228a9aae92f17191 >>>>>> >>>>>> >>>>>> >>>>>>> (this is my first time >>>>>>> checking out gist - wow!). >>>>>>> >>>>>>> HTH, >>>>>>> David >>>> >>>> On Fri, Jul 25, 2008 at 8:30 AM, Matt Lins <mattlins at gmail.com> wrote: >>>>> Yes, gist is great! >>>>> >>>>> Thank you very much for taking the time to look at this. I like your >>>>> suggestions very much and will use them. At this point I''m just >>>>> messing around, but I don''t understand why this doesn''t work. >>>>> >>>>> One more bad implementation if you have time: >>>> >>>> I don''t really have much - packing for a week''s holiday. >>> >>> Ok, thanks anyway. Enjoy your holiday. :) >>> >>>> >>>>> http://gist.github.com/2372 >>>>> >>>>> I''m removing the constant after each spec runs and redefining it >>>>> before each runs. The second spec still doesn''t pass though, even >>>>> though the constant is defined before each spec. >>>> >>>> Is is the same failure? >>> >>> Yes. >> >> What about when you run them each individually? >> >> spec migration_spec.rb - e ''should get a count of the records'' >> spec migration_spec.rb - e ''should find the records'' > > I actually just did that myself and they both pass separately - so > there is some state-leaking problem. I don''t really have the time to > investigate this now, but this is beginning to feel a lot like > "doctor, it hurts when I bang my head against the wall like this ...." > >> >> >>> >>>> >>>>> Again, I realize this is horrible, but it should still work, no? >>>> _______________________________________________ >>>> rspec-users mailing list >>>> rspec-users at rubyforge.org >>>> http://rubyforge.org/mailman/listinfo/rspec-users >>>> >>> _______________________________________________ >>> rspec-users mailing list >>> rspec-users at rubyforge.org >>> http://rubyforge.org/mailman/listinfo/rspec-users >>> >> > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
On Fri, Jul 25, 2008 at 9:30 AM, Matt Lins <mattlins at gmail.com> wrote:> Yes, gist is great! > > Thank you very much for taking the time to look at this. I like your > suggestions very much and will use them. At this point I''m just > messing around, but I don''t understand why this doesn''t work. > > One more bad implementation if you have time: > > http://gist.github.com/2372 > > I''m removing the constant after each spec runs and redefining it > before each runs. The second spec still doesn''t pass though, even > though the constant is defined before each spec. > > Again, I realize this is horrible, but it should still work, no?I think you''re right, there is something funky going on, and I *think* it''s something internal to RSpec. Your code looks like it ought to work, and it works in Mocha. I''ve poked around a little bit, but nothing jumped out at me. That said, this is a pretty weird edge case and afaik has not affected anything I''ve ever done. I can tell you that this is an uber-low priority, for me at least, so you''ll either have to hope it piques someone''s curiosity enough to fix, or go ahead and work on a fix yourself. As you''ve pointed out, you can do class MyModel; end MyModel.stub!(:count).and_return 1 MyModel.stub!(:find).and_return [@record] I''ve just committed a small enhancement that lets you do MyModel.stub!(:count => 1, :find => [@record]) in order to cut down on the noise. Download the latest source and build your gem and you''re good to go. Also, you can do as Scott suggested and use DI. Then you''d be doing something like Migration.set_model MyModel I agree though that this may not be desirable in some cases. For instance, if the constant name is represented as a string (such as in a DB column) then you''ve now put the burden on client code to do the string->constant conversion. Pat
Yah, I looked around. I came out with a better understanding of rSpec''s mocking internals, but no answer to the problem. I don''t need it. I''ll probably end up using FlexMock or Mocha for future projects anyway. I''d be happy to file a ticket if you think it''s worthy of one. It was my first implementation of the spec and I thought it should work. I''ve since refactored with Dave''s suggestions. I''m not so sure about DI in this scenario. I couldn''t find any decent examples of it''s use in rSpec and I''m happy with what I have now. Thanks again everyone. On Fri, Jul 25, 2008 at 11:28 AM, Pat Maddox <pergesu at gmail.com> wrote:> On Fri, Jul 25, 2008 at 9:30 AM, Matt Lins <mattlins at gmail.com> wrote: >> Yes, gist is great! >> >> Thank you very much for taking the time to look at this. I like your >> suggestions very much and will use them. At this point I''m just >> messing around, but I don''t understand why this doesn''t work. >> >> One more bad implementation if you have time: >> >> http://gist.github.com/2372 >> >> I''m removing the constant after each spec runs and redefining it >> before each runs. The second spec still doesn''t pass though, even >> though the constant is defined before each spec. >> >> Again, I realize this is horrible, but it should still work, no? > > I think you''re right, there is something funky going on, and I *think* > it''s something internal to RSpec. Your code looks like it ought to > work, and it works in Mocha. I''ve poked around a little bit, but > nothing jumped out at me. > > That said, this is a pretty weird edge case and afaik has not affected > anything I''ve ever done. I can tell you that this is an uber-low > priority, for me at least, so you''ll either have to hope it piques > someone''s curiosity enough to fix, or go ahead and work on a fix > yourself. > > As you''ve pointed out, you can do > class MyModel; end > MyModel.stub!(:count).and_return 1 > MyModel.stub!(:find).and_return [@record] > > I''ve just committed a small enhancement that lets you do > MyModel.stub!(:count => 1, :find => [@record]) > > in order to cut down on the noise. > > Download the latest source and build your gem and you''re good to go. > > Also, you can do as Scott suggested and use DI. Then you''d be doing > something like > Migration.set_model MyModel > > I agree though that this may not be desirable in some cases. For > instance, if the constant name is represented as a string (such as in > a DB column) then you''ve now put the burden on client code to do the > string->constant conversion. > > Pat > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >