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 > _______________________________________________ rspec-users mailing list rspec-users@rubyforge.org http://rubyforge.org/mailman/listinfo/rspec-users