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