Hitting the Middle Ground Between Classicist and Mockist TDD

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.

1
2
3
4
5
6
7
8
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 …

EDIT 20/08/2014:

I eventually moved this code into its own gem, it’s on github.

Comments