Design By Contract testing

The problem:

Recently, I had to write JUnit tests for a large date/calendar class CDate
in our project. This is quite a large piece of code, containing about
one hundred small methods. (We have written our own class because java.util.Date & java.util.Calendar are quite a mess and we also needed some specific, Polish hollidays and other stuff. (And our class is a mess, too.))

CDate has many similar methods that test some date attribute (isMonday(), isThursday()) or get various attributes (getMonth(), getDay()). This posed some problem: how do you test similar methods, assuring that they will undergo the same tests but without unnecessary code duplication? This article presents a solution.

A bad solution

The most simple approach would be just copy & paste test code for each method, adjusting necessary details. But this is bad. This is evil. A maintainer’s nightmare.

Next thing I thought about was reflection. The general idea was to make one generic test method (like checkIsSomeWeekDay()) and have other test methods (testIsMonday(), testIsThursday()) call it, using the method name as a parameter. This would look like this:

The code works as follows: for every date in predefined test set (datesForTest) it invokes (via reflection) method specified as parameter. It also gets the tested value from some other method. For every date, they should agree whether it is a Monday or not. This is verified by assertEquals in last statement of the loop. The real test is of course much bigger. I also skipped tests for other week days.

This approach works, but has many drawbacks and limitations:

Reflection prevents compiler from checking your code. If you change signatures of tested methods you won’t know your tests are broken until your run them.

You must analyze the code very carefully to see what’s called and how. No browsing tools will help you. (How much time do you need to tell what is the return type of isMonday()? Does it have any parameters? Is it static or instance method?)

Code looks clumsy and arcane and is tedious to write.

Tested methods must all have exactly the same signature (or the reflection call will fail at runtime).

The last drawback is the biggest — this approach won’t work for some more advanced methods, as we’ll see shortly (unless we make code much more clumsy and arcane, of course). In the real life example I had some very similar methods but some of them were static and other were instance methods (this was clearly a design flaw, but I wasn’t allowed to make any corrections to CDate until I write tests).

So there must be some better way. There is: using interfaces and anonymous classes instatiations.

A better solution

This is a classic form of adding "another abstraction level", but also can be thought of designing a contract between the generic test method and specific test methods that call it. In the reflection approach the contract was hidden deep beneath the code. Now we move it to the front. The generic method now asks its user (the specific method) for all the things it needs to know instead of extracting them in some black magic rituals.

So what does it look like? Let’s start by redesigning the checkIsSomeWeekDay() method.

This looks a lot nicer. Gone are the tons of exceptions, reflection method invocations and so on. Instead, the methods expectations are clearly documented: it gets a WeekDayHelper object, that has getValue() and getExpected() methods. getValue() is supposed to call some real CDate method to get appropriate value and getExpected() should return, well, expected value for this date. (And if we changed those methods’ return type to Object, we could test with this simple method almost any getter in any class.) WeekDayHelper is an interface that is also all about simplicity:

public interface WeekDayHelper{ boolean getValue (CDate date);

boolean getExpected (CDate date);}

This interface can be inner interface or can be moved outside of the test class. I prefer inner interfaces in this case, but it doesn’t make any significant difference.

Anonymous class instantiation

Now, for the last part: how do we call our new checkIsSomeWeekDay() method? This may look a little more convoluted than the previous version, but in the long run, it’s worth it.

What we see in testIsMonday() is anonymous class instantiation. We create a new object of type WeekDayHelper, but it’s an interface so we must provide implementations of all the methods inline (Java allows this syntax since version 1.1). Effectively we create an object of anonymous use-once-and-throw-away class and hand it to checkIsSomeWeekDay(). This syntax is also popular for writing various event handlers and if you have done some GUI programming you most probably have used it.

We can also leverage inheritance to move the getExpected() method to common superclass and parametrize it to avoid code duplication:

As we can see, testIsMonday() contains now only what is necessary to call date.isMonday(). The other method is in base class and the only thing it needs, Calendar.MONDAY constant is sent by constructor parameter.

This may seem as a lot of coding overhead in such a simple example, but it’s a tool that’s applicable to much more complicated cases. (And besides, who would like to read an article with thousands lines of code examples?)

A slightly bigger example

Let’s look at a final example that shows how we can create a common test for methods equals(), compareTo(), after() and before().

A quick analysis: ComparisonHelper interface still contains two methods. The compareDates() method is supposed to call some comparison method on its arguments (like d1.before(d2)) and compareStrings() should return expected value of that comparison (perhaps performing the comparison in some other way).

checkComparison() is the generic test method, it runs two nested for loops to compare each date with each other. We also need an array of strings that represent the same dates to get expected values easily (the strings are in format "YYYY-MM-DD HH:MM:SS", so they can be compared). For each pair of dates, checkComparison() calls compareDates() and compareStrings() of appropriate helper object to verify that underlying CDate method works correctly.

testAfter(), testBefore(), testCompareTo() and testEquals() call checkComparison() with anonymous classes that implement ComparisonHelper and use appropriate methods of CDate. As we can see, CDate.compareTo() returns an int while the other methods return a boolean. This is no problem for us, as I promised earlier.

Recalling list of drawbacks and limitation of reflection approach, we see that none apply:

Compiler now verifies that all of the method calls are correct. There are only explicit calls.

Your tests are easily understandable and browsable.

Code is less clumsy and less tedious, but it may seem at first that there is more code to write. It’s mainly because of Java verbosiveness. We have also explicitly documented our contracts.

Tested method signatures may differ in any aspect.

Happy coding and testing!

Related

Content Team

The IndicThreads Content Team posts news about the latest and greatest in software development as well as content from IndicThreads' conferences and events. Track us social media @IndicThreads. Stay tuned!