21 October 2006

[jMock] Listening to Opinionated Software

David Heinemeier Hannsen coined the pithy term Opinionated Software to describe the design philosophy he follows in the development of Ruby on Rails:
Rails is opinionated software. We make things that we consider good style easy to do and bad style hard. Or rather, we make good style beautiful and bad style ugly.
We have followed a similar approach in writing jMock. We describe mock objects and jMock as a design tool. jMock is designed to make some things easier to test than others so that doing test driven development with the jMock framework guides your code towards a style that we have found makes it easy to maintain in the long term and adapt to new, unexpected requirements. Code that is easy to test is composed of loosely coupled, cohesive objects and dependencies between parts of the system are made explicit and highly visible. Yes, you can use jMock's CGLIB plugin or Aspect Oriented Programming to get around jMock's limitations: to mock concrete classes and static methods or change the values of singletons and other global variables during a test, for example. However, we've found that when we find a need for one of those tools it's better to address the design problem than the testing problem. When the design problem has been addressed, the testing problem has gone away. We describe this as listening to the tests.

16 October 2006

When to ask instead of tell?

Mock objects work best with, and are designed to support, object-oriented code that is written in a Tell, Don't Ask style. However, that doesn't mean objects must, or even can, never ask each other for information. But apart from value objects and generic collection types, neither of which are stateful domain objects, I've found that I only need queries to search, sort and filter collections of objects. For example to filter non-managers out of a list of employees I'd need an isManager method on the Employee class:
Set managers() {
    Set managers = new HashSet();
    for (Employee employee : employees) {
        if (employee.isManager()) {
            managers.add(employee);
        }
    }
    return managers;
}
To use mock objects to test a method that only applies to managers you would stub calls to the isManager method of employees in the collection:
public void testOnlyAllowsManagersToTravelBusinessClass() { // for example
    Employee peon = mock(Employee.class, "peon");
    Employee manager = mock(Employee.class, "manager");
    
    Company company = new Company();
    company.addEmployees(peon, manager);
    
    expects(new InAnyOrder() {{
        allowing (peon).isManager(); will(returnValue(false));
        allowing (manager).isManager(); will(returnValue(true));
        
        ...
    }});
    
    ...
}
Queries should not require train-wreck expressions because that couples the object doing the selecting or sorting to the structure of the objects in the collection. The following code is a bit dodgy. The train-wreck expression will make code difficult to test with mock objects because a test will need to create a mock for each link in the chained expression.
Person findAManager(List employees) {
    List managers = new List();
    for (Employee employee : employees) {
        if (employee.getJobDescription().getRoles().contains(Role.MANAGER)) {
            managers.add(employee);
        }
    }
    return managers;
}
Introducing an isManager method on the Employee class makes testing easier and express the intent of the code more clearly.

12 October 2006

An unsung benefit of Tell, Don't Ask

The Tell Don't Ask programming style forces you to represent and name interactions between objects. This is a great help during maintenance. The code better expresses what it is doing, not just how it is doing it. The following code promotes an employee to be manager of their department:
employee.getJobDescription().getResponsibilities().add(new ManagementResponsibility(employee.getDepartment()));
But a Tell Don't Ask style forces us to state that explicitly:
employee.promoteToManager();

11 October 2006

How do you mock casts?

An occasional question that comes up on the jMock users' mailing list is how to mock casts? This is, I guess, mostly needed when interfaces are used to define capabilities of an object that are dynamically discovered by some other object in the system, somewhat like this:
public class Juicer {
    public void juice(Juiceable fruit) {
        if (fruit instanceof Peelable) ((Peelable)fruit).peel();
        fruit.juice();
    }
}
If you create a mock Juiceable object when testing the Juicer, how can you create an expectation for the cast to Peelable and make it return a mock Peelable? Casts and the instanceof operator are implemented at the language/VM level and cannot be mocked out. However, you don't need to mock them at all . You can instead define an interface in your test that can be cast to the required interface and mock that.
public class JuicerTest extends MockObjectTestCase {
    interface PeelableAndJuiceable implements Peelable, Juiceable {}

    public void testPeelsPeelableFruitBeforeJuicing() {
        PeelableAndJuiceable fruit = mock(PeelableAndJuiceable.class, "fruit");
        Juicer juicer = new Juicer();

        expects(new InThisOrder() {{
            exactly(1).of (fruit).peel();
            exactly(1).of (fruit).juice();
        }});

        juicer.juice(fruit);
    }
}
David Saff makes a good point: "[You might] find that the new interface captures a new abstraction in your code that is useful in other places. In my experience, if it's worth testing, it's often worth naming."

10 October 2006

Tell Don't Ask and Mock Objects

In my experience, object-oriented code is easier to understand and maintain if it is written in a "Tell, Don't Ask" style. In this style, objects make decisions using only the information that they hold internally or that they receive as message parameters; they do not make decisions using information that is held by other objects. That is, objects tell each other what to do by sending commands to one another, they don't ask each other for information and then make decisions upon the results of those queries. The end result of this style is that it is easy to swap one object for another that can respond to the same commands but in a different way. This is because the behaviour of one object is not tied to the internal structure of the objects that it talks to. How does that apply to Mock Objects? If objects interact by sending each other commands, their public interfaces provide no methods that let you interrogate their state. The only way to observe the behaviour of an object is to see how it affects the state of its world by sending commands to other objects. And that's what Mock Objects let you do. The extreme opposite of the "Tell, Don't Ask" style is "train-wreck" code that contains lots of statements like "object.getPart().getSubpart().getAttribute()" and "object.getPart().getSubpart().setAttribute(x)". In coding style the implementation of one object is coupled to the structure of its neighbours and its neighbours' neighbours, and so it is difficult to replace its neighbour with one that is implemented differently. This style of code is hard to test with Mock Objects. You find yourself creating lots of mock objects that only exist to let the object under test reach the objects that it actually uses. That's a strong sign that the code needs refactoring: you can simplify the code by introducing new methods in the immediate neighbours of the object under test.

09 October 2006

[jMock] Keywords and values

Someone recently asked us about the jMock syntax. Why do we write: mockFoo.expects(once()).method("bar").with(eq(1)).will(returnValue(99)); rather than, say mockFoo.expects().once().method("foo") etc.. The methods expects, method, with, and will are defined on our builder interfaces and chained on the same object. On the other hand, once, eq, and returnValue are attached to the container—MockObjectTestCase. One way to think of it is that our chained methods are keywords in our Domain-Specific Language, they're the fixed part of the language. The container methods represent values, points where users of the language can write their own extensions. In jMock, all these values are either a Constraint or a Stub. We provide common implementations of these interfaces, but users can write their own to add a behaviour that is meaningful within the domain of their application.

Between not inside

A frequent question from new users on the jMock users' mailing list is how to use jMock to mock out one method of an object while testing another method of the same object. The scenario usually involves inheritance: how can one mock out a superclass method to test the subclass. A recent query had a subtle twist: the user wanted to mock out an interface method, but that interface method was implemented by a superclass and called by a subclass, as follows:
public interface I {
    public void doSomething();
}

public abstract class A implements I {
    public void doSomething() {...}
}

public class B extends A {
    public void doLotsOfStuff(){
        ...
        doSomething();
        ...
    }
}
Mock objects are a tool for designing/testing the interactions between objects. In the code above class B is calling doSomething on itself. There is no need to use mock objects to test the B class. Tests for that exercise the functionality of doLotsOfStuff should not care that it calls doSomething somewhere down the line. That's just an implementation detail.

04 October 2006

[jMock] You don't always have to repeat expectations.

Here's a question that came up at work today. One team wanted to assert in a test that the target code would call a service three times and get back a series of values. Their first thought was something like this:
thing.expects(once()).method("foo").will(returnValue(99)).id("first");
thing.expects(once()).method("foo").after("first").will(returnValue(21)).id("second");
thing.expects(once()).method("foo").after("second").will(returnValue(17));
which uses the expectation identifier to enforce sequence. A more compact version looks like this:
thing.expects(exactly(3)).method("foo")
  .will(onConsecutiveCalls(returnValue(99), returnValue(21), returnValue(17)));
This will fire each stub (returnValue()) in turn when foo() is invoked, in this case returning the series of values. Now, after the fact, I wonder if they should have stubbed the call:
thing.stubs().method("foo").will(onConsecutiveCalls(returnValue(99), returnValue(21), returnValue(17)));
It looks like the call to foo() is supporting infrastructure, not part of the specification of the behaviour of the target code. I'll have to check in the morning.