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' > >> >>> 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