Martin Fowler has blogged on a testing technique he calls TestInvariant.

This reminded me of a related approach that I have used is to refactor assertion methods onto the class under test. Here is how I got there.

Suppose you are was writing some tests on Martin's bowler class:

public void testConcedingRunsAddsToRunsScore() {
  Bowler botham = new Bowler();
  assertEquals(0, botham.getRuns());
  botham.concedeRuns(4);<br />
  assertEquals(4, botham.getRuns());
}

Now there are 2 code smells here to me.

First, the test has forced me to expose the internal implementation of the Bowler class - bleargh. It is bad enough in domain objects and it is worse when it is just for the purpose of testing. Even if we make the getter package protected it still ollutes the API. And sooner or later someone will come along who will make it public and start using it.

Second, the assertions are not very domain specific - and there is duplication. Lots of developers will make them a bit more informative with a message - like this:

public void testConcedingRunsAddsToRunsScore() {
  Bowler botham = new Bowler();
  assertEquals("Should have 0 runs", 0, botham.getRuns());
  botham.concedeRuns(4);
  assertEquals("should have 4 runs", 4, botham.getRuns());
}

I tend not to do this for assertEquals - it is already informative enough, and the message doesn't increase readability, it increases duplication. So lets do something else:

public void testConcedingRunsAddsToRunsScore() {
  Bowler botham = new Bowler();
  assertHasRuns(botham, 0);
  botham.concedeRuns(4);
  assertHasRuns(botham, 0);
}

private void assertHasRuns(Bowler b, int runs) {
  assertEquals(runs, bowler.getRuns());
}

That's nice - we have a new informative assertion that is now making an assertion about the domain, not about the state of the object.

If you look at the testConcedingRunsAddsToRunsScore method, everyhting it does is expressed in the language of the domain - we have a mini domain language right here. Consider the assertion assertHasRuns(botham, 0) - that reads nicely.

But now there is one more code smell - feature envy. The assertHasRuns method is envious of the bowler, and following the precepts of refactoring - we should listen to what the code is telling us and move the method onto the bowler. so lets do that:

class Bowler {
  public void shouldHaveRuns(int expectedRuns) {
    Assertion.assertEquals(expectedRuns, this.runs);
  }
}

I've done one extra step here, and renamed the assertion to make the domain language a bit clearer. Let's look at the test now:

public void testConcedingRunsAddsToRunsScore() {
  Bowler botham = new Bowler();
  botham.shouldHaveRuns(0);
  botham.concedeRuns(4);
  botham.shouldHaveRuns(4);
}

There is one extra really nice thing now - since the assertion has moved onto the class under test, we no longer need to expose the getRuns method. So we can remove it and increase the encapsulation of the class.

This approach also fits quite nicely with the emerging approach of Behaviour Driven Development and Humane Interfaces.

Note though that as shown above, you need to distribute the JUnit classes with the production code, to make the assertion methods object available. I don't mind this. If you do, you could use Java's own assert support, or move the assertions onto a testing subclass of the original object.

This is a technique that I have used to "re-encapsulate" objects in legacy systems. Often there are objects that give all their information away through getters, initially for testing and then slowly people start using the methods rather than encapulating behaviour. This allows you to remove the getters incrementally.