From Martin Fowler’s point of view, I must have been a mockist. With using mocks extensively comes quite a few advantages :
- test failures often pinpoint the falsy code
- easier test organisation mimicking that of the code
- faster tests
- simpler test initialization
Mocks also have their own problems, but mostly :
- especialy with dynamicaly typed languages, a mock for class A might not implement the same methods than the real class A, but the test might be passing though ! This ampers refactoring with a longer feedback loop and mock setup rewriting
That’s what always bothered me. Eventually I tried a combinaison of techniques that seem to work well together and provides most of the best of both worlds.
- extensive use of factories (with FactoryGirl) to simplify setup
- use of an in memory sqlite database to get a fast full working db
- implement fully functional fakes for some parts of the system
- carefull use of mocks, inspired from Gregory Brown’s thoughts on mocks
- when a test is too slow
- to cut off a dependency to a subsystem that is not available in a unit test
- to simplify overly long test data setup
- use of test proxies (as in rr) to inject specific behaviour or to perform specific checks without modifying the rest of the program.
Here is how I implemented this with rspec :
With all this in place, it is most of the time possible to write straightforward tests. For example here, only real objects are used. @order gets its value when Order.create! is called.
it "should create an order with the cart" do capture_result_from(Order, :create!, into: :order) check_in_cart @order.should_not be_nil @order.cart.should == @cart end
Maybe I should swith to rr …
I eventually moved this code into its own gem, it’s on github.