Pat, Thank you very much for the link, and dependency injection explanation. That helps a lot. I like your blog, too.
Brandon > -----Original Message----- > From: rspec-users-boun...@rubyforge.org [mailto:rspec-users- > boun...@rubyforge.org] On Behalf Of Pat Maddox > Sent: Saturday, April 11, 2009 7:25 PM > To: rspec-users > Subject: Re: [rspec-users] Mocking: brittle specs and tight coupling? > > On Sat, Apr 11, 2009 at 3:59 PM, Brandon Olivares > <programmer2...@gmail.com> wrote: > > Hi, > > > > I've read of complaints that mocking can make the specs very brittle, > in > > that if the method is changed at all, it will break the specs, even > if the > > behavior remains the same. Do you find this to be the case? How do > you deal > > with this if so? > > http://patmaddox.com/blog/you-probably-dont-get-mocks > > > > Also, when I used to do TDD in PHP, well there wasn't the ability to > modify > > the class on the fly like in Ruby, so you actually had to do > dependency > > injection, but people generally looked at this as a good thing, for > loose > > coupling. So if a controller method for instance used a User model, > then it > > would be preferred to get that instance from somewhere, either > passing it to > > the method itself or calling another method to instantiate it. > > > > I notice this isn't a concern in RSpec or in Ruby in general. Do you > view > > this differently, or what is the preferred way to deal with > dependencies? Is > > it fine just to do: > > > > User.should_receive(:new).and_return(User.new) > > > > Just as a very simple example? > > I have an example of this in my Legacy Rails talk and say it's the > sort of thing that would make a Java programmer run for the fucking > hills. That's not entirely true because there are a couple mock > frameworks that do let you do that, but in general they prefer to > avoid it because it requires bytecode manipulation. > > Ruby is much more flexible and gives us a couple ways of injecting > dependencies. You've got traditional DI: > > class Order > def calculate_tax(calculator) > calculator.calculate total, us_state > end > end > > You've got traditional DI + default args > > class Order > def calculate_tax(calculator=TaxCalculator.new) > calculator.calculate total, us_state > end > end > > You can partially mock on the target object: > > class Order > def calculate_tax > calculator.calculate total, us_state > end > > def calculator > TaxCalculator > end > end > > order = Order.new > order.stub!(:calculator).and_return(mock('calculator', :calculate => > 1.25)) > > or you can use partial mocks somewhere inside of the target object, > like you showed. > > The pattern you showed is popular because often you won't ever want to > pass in a different object. In your UsersController you're only ever > going to deal with the User class as a repository, and if you change > it then it's a fairly big change and you don't mind updating your > tests. > > I find that you can use mocks to express the intent of the class well. > Don't use constructor/setter/method dependency injection if you don't > need it...accept a bit tighter coupling and use partial mocking if all > you're trying to do is isolate behaviors. > > Pat > _______________________________________________ > 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