UnitTestsAndCoupling

September 20, 2004
A colleague recently took me to task on the following over-statement from my XP paper (see also WhyUnitTest):
Requiring code to be responsive to multiple clients [unit test and other production code] forces less coupling of objects which promotes long term flexibility.

His point was that someone writing a unit test on a piece of tightly coupled code would have to reproduce the coupling, and if unaware, would increase the coupling by hitching the unit test to both classes. He and I discussed some example code via email:
Here's your example again:
    class MyClass {
...
public String prependAppPath(LogFile file)
{
return m_sPathToApp + PATH_SEP + file.getFilename();
}
...
}

// client code
String sFullPathAndFilename = prependAppPath(myLogFile);

MyClass is coupled to LogFile, probably unnecessarily, at least for the prependAppPath method.

Here's how I might start a unit test for it:
    public void testPrependAppPath()
{
MyClass obj = new MyClass();

obj.setPathToApp("c:/temp");
obj.prependAppPath(...)
}

“Dang, I need a LogFile instance now. Bummer, why is MyClass coupled to LogFile?”

This is the sort of coupling that is exposed by the unit test, but, of course it's only exposed if the developer takes notice.

Let's continue... “Oh well, let's carry on.”
    import com.ei.util.LogFile;

public void testPrependAppPath()
{

MyClass obj = new MyClass();

obj.setPathToApp("c:/temp");
LogFile log = new LogFile();
log.setFilename("mylog.log");

assertEquals("c:/temp/mylog.log", obj.prependAppPath(log));
}

Here I've proven your point, that the unit test does not force a de-coupling, and now it's worse, because we have MyClass and MyClassTest both coupled to LogFile. But the test passes, so now let's jump into a refactoring.

First, a failing test:
    //import com.ei.util.LogFile;

public void testPrependAppPath()
{
MyClass obj = new MyClass();

obj.setPathToApp("c:/temp");
//LogFile log = new LogFile();
//log.setFilename("mylog.log");
assertEquals("c:/temp/mylog.log", obj.prependAppPath("mylog.txt"));
}

This won't even compile, so we know the test is failing. Fix the prod class.
    public String prependPath(String sFilename)
{
return m_sPathToApp + PATH_SEP + sFilename;
}

Test should still pass, and now, unless there are other couplings between MyClass and LogFile outside our example, we can eliminate the import clause from MyClass as well, and bye-bye coupling.





All this to say, he makes a good point, and I think I need a re-write of my original sentence. Something like:
Requiring code to be responsive to multiple clients helps to expose coupling of objects. The more times a coder has to deal with the coupling, the better the chance it'll get noticed and dealt with properly, which helps promote long term flexibility in the code.
The point raised in the new sentence, of course, is the importance of having coders who understand all this. No process can survive against inexperienced and/or sloppy people. “You can't fix stupid.” see TheKeyToSuccess.

tags: ComputersAndTechnology AgileDevelopment