I was writing a new feature that deals with lots of Files for my favorite open-source tool, a database front-end known as
SQuirreL SQL Client. Now, I'm a believer that unit tests should be written for most code as it helps you develop loosely coupled, highly cohesive code. So I set about to test my new feature that creates and deletes files using the Java file API (java.io.File). Alas, java.io.File has no interface and forces you to work with
actual files! Of course I don't want my JUnit tests creating and deleting files - that would be a nightmare and just create test portability issues. I said to myself, wouldn't it be nice if the java.io.File API had a file factory interface that created file interfaces instead of concrete Files. Then I could simply inject my factory implementations whipped up by my favorite mocking tool (
EasyMock) which would create file implementations that I could set expectations on. Well, if you have to use concrete classes, you could certainly wrap them in an implementation that implements an interface and is created by a factory. Hmmm... Eureka! So I wrote FileWrapper to be the interface for FileWrapperImpl - which wraps java.io.Files - and gave it the same interface as java.io.File. It took me about 20 minutes to generate the code thanks to Eclipse. And now I have a drop-in replacement for java.io.File that allows abstract away my dependence on java.io.File and test my code to my heart's content without having to access a single file on my filesystem. The code is LGPL, located in CVS
here and consists of the following classes (Click each to download):
FileWrapper.java - same interface as java.io.File
FileWrapperImpl.java - implementation of FileWrapper
FileWrapperFactory.java - interface for the factory that creates FileWrapper
FileWrapperFactoryImpl.java - implementation for FileWrapperFactory
For example, suppose you have some code that simply news up files like:
public void testFile() {
File myFile = new File("/path/to/file");
if (myFile.exists()) {
doSomething();
}
}
It's kind of hard to test that doSomething get's called if File myFile exists and doesn't get called when it doesn't exist without creating and removing the file located at "/path/to/file". So re-write the code like this:
FileWrapperFactory factory = null;
public void setFileWrapperFactory(FileWrapperFactory factory) {
this.factory = factory;
}
public void testFile() {
FileWrapper myFile = factory.create("/path/to/file");
if (myFile.exists()) {
doSomething();
}
}
This allows you to inject the FileWrapperFactoryImpl from other classes for production code and allows you to inject a Mock FileWrapperFactory when unit testing. Notice how there is very little change to the existing code. That is mostly due to the FileWrapper interface having the same declared methods that are found in the concrete class java.io.File.
Enjoy!
No comments:
Post a Comment