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