Archive for March, 2007
I don’t normally do personal on my blog, but for those who know me personally, our second child arrived this morning, Oliver. Here is the obligatory sleeping photo.
The labour was short, the birth went fine and Mum & baby are doing great.
Ben & I were discussing Instinct’s “verification” API (i.e. JUnit’s Assert) the other day when he made the observation that what we were developing was the same as what we are already doing with mocking APIs. At first I wasn’t convinced, but the more I thought about it, the more it makes sense.
What we are doing with JUnit’s Assert class is making state-based assertions, and with mocking frameworks making behaviour-based assertions. So with Instinct what we intend to do is unify the state and behaviour expectations into a single unified Expectation API.
We toyed with the naming for a while, but “expectations” make more sense than “verification” or “assertion”. It also fits in better with the experimental process (i.e. driving) when doing BDD, using words such as “must” is a bit too harsh, whereas “should” implies that you don’t really have to do it. “Expect” is a much better way to say what you really mean; “expect that foo.bar() is called”. I initially wanted to call it “ensure”, as in “ensure that foo.bar() is called”. However the naming doesn’t quite roll off the tongue, and I thought it may get confused with “insure” in conversation.
At the day job, we’ve been working with a well known open source project in order to simplify our interface to some infrastructure (that our code must use to interact with vendor code). The guys who run the project have been really good, and we’ve been speaking to them on a regular basis, giving (and taking) – amongst other things – advice on how the project can be improved. Today’s conversation centred around their testing infrastructure, where we hit upon a thorny subject that crops up quite a lot – deep inheritance hierarchies.
Our problem is that we have our own JUnit testing infrastructure (i.e. inheritance hierarchy) and the open source project has one too, and as we can only inherit from one parent in Java we’re stuck. The issue comes about as the test classes that we need to use cannot be delegated to; they make the assumption that they are the test being run (which is important to them), which in our case isn’t true (they’d be a field in our class). One option of course is to inherit from them, and delegate to our classes. This however highlights several common problem with locking code up using inheritance.
- Using the hierarchy binds you to all the parents of your immediate super class, even if they offer functionality that you don’ want.
- The general testing infrastructure is tied to single implementation – JUnit, and hence cannot be used with other frameworks such as TestNG or Instinct.
- Subclasses can inadvertently break the functionality of their parent, say by implementing
runBare()and not calling
super.runBare(). As JUnit has quite a few extension points so without perseverance it isn’t easy to know which one to override in what circumstances.
- Inheriting can increase your exposure to a framework. So for example, if you dump all of you code into a TestCase subclass, your interface to JUnit is quite thick. You can easily get around this by making the subclasses as thin as possible, and quickly delegating out to your real classes. In this way the subclass is only there as an interface to the framework, and you can re-use your real code elsewhere. In the case of open source projects this is even more important, as your clients can reuse your code as well.
- All the functionality is locked up in the superclasses, reducing your ability to use it outside of the hierarchy.
- Logically separating behaviour in superclasses limits the subclasses ability to decide what behaviour it want to inherit, and how. In our example, the superclasses perform certain functions, in a certain order, which the inheritance gives you for free. However, as a subclass you cannot decide if, or in what order you want to use the behaviour.
- But perhaps the most important reason is that all the benefits of inheritance can be achieved by other means; delegation, composition, static imports, decorators, etc.
All of these reasons contribute to testing frameworks such as JUnit moving away from concrete inheritance to other mechanisms. With JUnit, it’s a
RunWith annotation that allows you to specify the runner to use to run the test. This is a much better way to interface with the framework, as it provides you with a limited interface to the framework (reducing clutter in your code) and provides the framework with the flexibility to be extended beyond its original design.