I think that's one of the nicest descriptions of the value of outside-in I've seen.
Thanks Zach. 2008/9/5 Zach Dennis <[EMAIL PROTECTED]> > On Thu, Sep 4, 2008 at 7:14 PM, Nick Hoffman <[EMAIL PROTECTED]> wrote: > > On 2008-08-27, at 15:25, Mark Wilden wrote: > >> > >> The other thing I would say is that mocking and stubbing are powerful > >> tools that you should add to your arsenal as soon as possible. I've had > >> several coworkers who resisted using them, only to finally achieve that > >> "aha!" moment later. Your tests get easier to write, and they're less > >> brittle to change. > > > > G'day Mark. I was re-reading this thread and noticed this paragraph of > > yours. I've been using RSpec and BDD for about 2 months now, and love it. > > > > However, I'm not a fan of mocking and stubbing, primarily for two > reasons: > > 1) I believe that specs should test behaviour, rather than a behaviour's > > implementation. > > This is a misleading statement. Testing the behavior of an object > involves its input and output collaborators. When used appropriately > mocks and stubs act as an agent for design and discovery. They also > allow objects under test to be isolated from other objects that it > depends on (which may not even exist yet). > > Collaborators are what should be used to set up with stubs and mock > expectations. This way we can test the behavior of the object under > test given different values returned by its collaborators. > > > For example: > > class FundsTransfer > def transfer(amount, source_account, target_account) > if source_account.balance < amount > raise "not enough money" > else > source_account.deposit amount > target_account.withdraw amount > end > end > > Without using mocks or stubs to test the above code I wouldn't be able > to test the case where the source account doesn't have enough money or > does have enough money -- unless I already have implemented the > Account class and its withdraw, deposit and balance methods. Taking > the approach of writing the Account class first is an approach that a > lot of developers take. But you build what you think you need before > you actually need it. I prefer to develop from the other direction, > only building what I discover I need to make it work. Either way > though behavior is being tested, and it is not testing against the > internal state of an object. > > > 2) Using mocks and stubs causes your specs and implementation to be > tightly > > coupled, which often forces you to modify your specs if changes occur in > the > > implementation. > > > > However, #2 contradicts what you said about "tests ... [are] less brittle > to > > change" when using mocks and stubs. Considering that I'm still very new > to > > mocks and stubs, I'm probably missing something here. When you have a > > minute, would you mind countering me? > > You can write bad tests with mocks/stubs and without mocks/stubs. > Using mocks/stubs doesn't immediately tightly couple your specs to > your implementation. It does however tie your specs to the external > interface of the object under test. If you change that, then you will > need update your specs. > > Consider our FundsTransfer example again: > > class FundsTransfer > def transfer(amount, source_account, target_account) > if source_account.balance < amount > raise "not enough money" > else > source_account.deposit amount > target_account.withdraw amount > end > end > > If I supply a source_account that has a stubbed out balance of less > than the amount requesting to be transferred am I tightly coupling the > spec to the implementation? No more so then creating a source account > fixture with an amount less than the amount that I am requesting to > transfer. I say this because at some point you need to test the > scenario where funds are requested to be transferred from a source > account that doesn't have enough money. > > Do you really see one of the below examples more tightly coupling the > test to the implementation? > > account = mock("Account", :balance => 0) > account = Account.new :balance => 0 > > The difference to me is that using the mock allows me to discover > earlier what interface I want to work with on the Account class. If I > go the non-mock route then I need to make sure I build the Account > class with the interfaces I need first. > > I much prefer to work in small steps. Focus on one behaviour, and then > when it works as expected, make sure any collaborators that need to be > implemented are. Then use my integration tests to make sure everything > is wired up correctly together. > > That's just me though, > > -- > Zach Dennis > http://www.continuousthinking.com > http://www.mutuallyhuman.com > _______________________________________________ > 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