On Fri, Jul 25, 2008 at 8:49 AM, David Chelimsky <[EMAIL PROTECTED]> wrote: > On Fri, Jul 25, 2008 at 8:44 AM, Matt Lins <[EMAIL PROTECTED]> wrote: >> On Fri, Jul 25, 2008 at 8:40 AM, David Chelimsky <[EMAIL PROTECTED]> wrote: >>>> On Fri, Jul 25, 2008 at 8:15 AM, David Chelimsky <[EMAIL PROTECTED]> wrote: >>>>> On Fri, Jul 25, 2008 at 7:57 AM, David Chelimsky <[EMAIL PROTECTED]> >>>>> wrote: >>>>>> On Fri, Jul 25, 2008 at 12:34 AM, Matt Lins <[EMAIL PROTECTED]> wrote: >>>>>>> On Fri, Jul 25, 2008 at 12:25 AM, Scott Taylor >>>>>>> <[EMAIL PROTECTED]> wrote: >>>>>>>> >>>>>>>> On Jul 25, 2008, at 1:15 AM, Matt Lins wrote: >>>>>>>> >>>>>>>>> On Thu, Jul 24, 2008 at 11:47 PM, Scott Taylor >>>>>>>>> <[EMAIL PROTECTED]> 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@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 <[EMAIL PROTECTED]> 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@rubyforge.org >>> http://rubyforge.org/mailman/listinfo/rspec-users >>> >> _______________________________________________ >> rspec-users mailing list >> rspec-users@rubyforge.org >> http://rubyforge.org/mailman/listinfo/rspec-users >> > _______________________________________________ rspec-users mailing list rspec-users@rubyforge.org http://rubyforge.org/mailman/listinfo/rspec-users