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.