Tag Archives: TDD

Do mocks create high coupling ?

In a recent discussion with a colleague, he bought up the age old myth that mock objects are bad because they cause brittle tests and pointed me to this post. My view is if using mock objects are causing brittle tests it is a hint that there is a issue with the design  of objects collaborating, which needs to be addressed. Mock object are a great tool that aid in the design of OO systems if you subscribe the “mystical view” of object oriented programming as described by these books RDD and GOOS Ralph Johnson notes “The mystical view is that an OO system is one that is built out of objects that communicate by sending messages to each other, and computation is the messages flying from object to object.”

lets start by looking at the example in this post

module Codebreaker
  describe Game do
    describe "#start" do
      it "sends a welcome message" do
        output = double('output')
        game = Game.new(output)
        output.should_receive(:puts).with('Welcome to Codebreaker!')
        game.start
      end
      it "prompts for the first guess"
    end
  end
end

with implementation code as show below.

module Codebreaker
  class Game
    def initialize(output)
      @output = output
    end
    def start
      @output.puts("Welcome to Codebreaker!")
    end
  end
end

While it very clear in the above example the implementation code simply duplicate the test code, and severely inhibit any re factoring with out breaking the test code. As the author of the blog post points out any of the following implementations are valid but yet they would break the test

@output.write("Welcome to Codebreaker!\n")

@output.print("Welcome to Codebreaker!\n")

"Welcome to Codebreaker!".split.each{|c| output.write(c)}; output.write "\n"

@output << "Welcome to Codebreaker!\n"

The key here is to “listen to the test”, there is duplication in test and implementation,  the test is highlighting a serious flaw in the inter-object protocol. One of the things I picked up over the years in various discussions  at London Xtreme Tuesday group is that an object should send messages to its collaborator in terms of its domain language. In the example above an instance of the Game object sends the message

:puts

to its collaborator. now :puts is a meaningless message in terms of the Game. With the mystical style of OO the domain model is in the message flying between the object.
One way to fix the above code is as following. start with an end-to-end test to verify the end result for a given scenario.

the unit test would be as follows

module Codebreaker
  describe Game do
    describe "#start" do
      it "notifies game has started" do
        gameEventListener= double('gameEventlistener')
        game = Game.new(gameEventListener)
        gameEventListener.should_receive(:new_game_started)
        game.start
      end
      it "prompts for the first guess"
    end
  end
end

The key thing to note here is that the message :new_game_started is meaningful in terms of the Game objects domain. The role (gameEvenListener) of its collaborator is now made explicit in the test. We can now have an implementation of this role with write out the appropriate welcome message when it receives :new_game_started . While this is very simplistic example and may not seem like there is a huge impact on example, In larger systems I find well designed inter-object object protocols leads to simpler flexible system where behaviour can easily change by composing different objects.

In chapter 6 of the RDD book the authors describe different control styles in a system.  I find that  mock objects is a tool that guides our design to a “delegated control style” , one of the key characteristic of this style is

“Dialogs between objects are at higher-level.Collaborations between a coordinator and the objects it coordinates are typically higher-level requests for work and not simple requests to store or retrieve data. Communications are more abstract and more powerful. Instead of asking objects to do small chores or divulge small bits of information, coordinators typically delegate larger-grained requests.”

At the same time if we distribute responsibilities across too many different objects we have the following side effects “Complicated collaborations between delegator and delegates. This can happen when not enough context is passed along with a delegated request. Lots of collaborations but not much work getting done.”

When using mock object the test will be very sensitive to these issues and will highlight these facts by being tightly coupled to implementation details of the object or by awkward tests

To summarize the mains points are we

  • need to understand the philosophies and ideas of OO  to effectively use and appreciate mock objects
  • Using mock object is a great feedback tool that can be used  to design high level dialogues between objects, brittle tests is usually an indication that the object design needs to be reviewed.
  • in an OO system the domain model is in fact in the messages sent between objects.
  • Object must send messages to it peers in terms of its domain language.