h1. Don't Mock Yourself Out By David Chelimsky The big issue here is when to use a mock (Martin Fowler) h2. Overview of stubs and mocks h3. Test double An object that is standing in for the real object like a stunt double in a movie. h3. Test stub customer = stub("customer") customer = stub(:name).and_return('Joe customer') h3. Mock object logger = mock("logger") logger.should_receive(:log).with(/Joe Customer/) h3. Method level customer = Object.new logger = Object.new We are stubbing out a method on customer, and setting an expectation on the logger- h3. Things aren't always as they seem A lot of examples are using stubs like mocks and visa versa. We could say that customer should_receive(:name), which will be an expectation and not just a stub If we change something in the code, the test will fail. h3. Stubs vs. mocks We verify stubs by verifying states after interactions We tell mocks to verify interactions Sometimes stubs just make the system run h3. When are method stubs helpful? Isolation from non-determinism Random values: We force a die to return a specifik value Time: Time.stub(:now).and_return(now) h4. Isolation from external dependencies: Network access Example: ActiveMerchant can run in a test-mode, where it interacts with the ActiveMerchant test-server. But this can be really slow, so we can mock the network out. h4. Polymorphic collaborators One class that accepts different strategies. We could use the real strategies to test, but we should rather create a mock strategy. h4. Mixins/plugins h3. When are method expectations helpful? Side-effects - logging for instance. Caching: Should only call this once, and then it shouldn't call it again, because it is cached Interface discovery: Create a mock for something that does not exist yet - In the process you create the interface for that h2. Mocks/stubs applied to Rails Spaghetti-code vs. ravioli-code - Rails is Calzone-code: We only have these three layers that we interact with, really MVC is not the whole picture. There is also the browser, router and database to consider. Unit-test: Testing something in isolation Functional and integration tests are sort-of switched in Rails - there is no right way It is not DRY to test everything several times. h3. Stubble - DRY controller testing stubbing(Registration) do post 'create' response.should redirect_to(registrations_path) end stubbing(Registration, :as => :invalid) Seperate library to it can be used in both RSpec and TestUnit h3. Chains User.stub_chain(:find, :friends, :favorite).and_return(friend) Many people will say this is horrible, but everybody does it, so we added to the latest version of RSpec. h2. Guidelines and pitfalls Avoid tight coupling Complex setup is a red flag for design issues Don't stub/mock the object you're testing: * There is a lot fo behaviour in the object we are testing * You are testing that object, you are not testing what will actually be running * But there can also be great benefits, but you need to be aware of how anything interacts with anything h3. What is behaviour? Software is like an onion. h3. False positives A name is changed in the model and the test, the controller test will not fail, because it has stubbed out the method The answer is to do some kind of high-level testing. I use cucumber. h2. Questions