Ketan's Musings

Where he blogs about his eclipse musings

Tell, Don’t Ask – Part 2

with 5 comments

Objects exposing behavior, not state

Controlling complexity of your codebase by limiting what state your objects expose

The more objects that can see and change states on other objects, the more complex your system. Objects returning a boolean mean that someone calling that method will use an if branch, returning an integer would mean someone using if/else or switch/case. Returning objects would mean introspeting that returned object to invoke something else on it. This increases coupling between classes, makes code hard to read and test.

My class has 3 friends, I talk to my friends’ friends. My friends are difficult to mock, therefore mocking sucks…

… well, yeah!

Testing procedural code is hard. Testing such code generally involves setting up “data” and asserting on state of objects. Tell Don’t Ask code on the other hand is easier to test since you’re not testing state. Also notice how DI makes things simpler to test.

void testOwnerCanFeedDog(){
    Dog dog = new Dog();
    // have to create a mouth since owner calls dog.getMouth() to feed it
    Mouth mouth = new Mouth();
    dog.setMouth(mouth);
    PetOwner owner = new PetOwner();
    owner.setDog(dog);
    owner.feedDog(food);

    // verify that the dog gets the food (well the mouth, actually)
    assertEquals(food, mouth.getFood());
}
void testOwnerCanFeedDog(){
    Dog dog = mock(Dog.class);
    PetOwner owner = new PetOwner(dog);
    owner.feed(food);

    // verify that the dog gets the food
    verify(dog).feed(food);
}

Without Dependency Injection, testing is quite difficult; without Tell Don’t Ask, testing is almost always impossible. Put together, things are separated, testing is simplified.

Written by Ketan

September 4th, 2009 at 8:16 am

5 Responses to 'Tell, Don’t Ask – Part 2'

Subscribe to comments with RSS or TrackBack to 'Tell, Don’t Ask – Part 2'.

  1. In your second test, how does the PetOwner know of the mocked Dog?

    Nick Stolwijk

    4 Sep 09 at 11:44 am

  2. I’m not a big fan of Mocks but the non mocked example test seems overly contrived. There is no reason to not use constructor injection in the first example or to have a Dog class that needs two phase construction. From Tell, Don’t Ask – Part 1 we know that the Dog has an isHungry query method, so I would have written the following test with no need for mocking.

    void testOwnerCanFeedDog(){
    Dog dog = new Dog();
    PetOwner owner = new PetOwner(dog);
    owner.feedDog(food);

    assertFalse(dog.isHungry())
    }

    Brian Swan

    4 Sep 09 at 2:24 pm

  3. @Nick,

    Thanks for catching this. The example is supposed to be using constructor based injection. I’ve updated the post accordingly.

    Ketan

    4 Sep 09 at 12:43 pm

  4. @Brian,

    With a mock dog, the owner need not have a real dog to feed to test if the owner can actually perform an operation.

    Ketan

    4 Sep 09 at 2:36 pm

  5. @ketan
    I think you are missing Brians point, he is saying your unmocked test is not realistic and provides a better example. Its not that he doesn’t understand that mocking allows you to replace dependencies :P

    I’d also argue that it is worth focussing on the key advantages of mocking. It isn’t just about making it easier to test, its about pushing forward your design. See mockobjects.com or “Mock Roles, Not Objects”.

    Having said that to me mocking domain objects, like dog/owner here, is often a mistak. I might well use state based testing in this sort of situation even if keeping PetOwner/Dog very loosely coupled is important, but thats a seperate discussion.

    Colin Jack

    6 Sep 09 at 10:14 pm

Leave a Reply

IMPORTANT! To be able to proceed, you need to solve the following simple math (so we know that you are a human) :-)

What is 15 + 8 ?
Please leave these two fields as-is: