Java – Wrapping utility classes and injecting them for unit testing purposes

javaobject-orientedunit testing

I found that it is so hard to test classes that depend on other utility classes
as java.nio.file.Files. It is also impossible to mock them using the classic unit testing stack (junit,mockito,..) unless you use other heavy mocking frameworks like powermock which i consider a bad practice in case of building projects from scratch and also a nightmare if you try to integrate it with maven plugins ( surefire, jacoco ,offline instrumentation,…)

So what i've decided to do to ease my unit testing, is to wrap this utility classes inside a normal instantiable class and inject them to my main class as follows:

public class IOManager {
    private final FileSystemUtilsWrapper fileSystemUtilsWrapper;
    private final HttpUtilsWrapper httpUtilsWrapper;

    public IOManager() {
        this.fileSystemUtilsWrapper = new FileSystemUtilsWrapper();
        this.httpUtilsWrapper = new HttpUtilsWrapper();
    }

    public IOManager(
            FileSystemUtilsWrapper fileSystemUtilsWrapper, 
            HttpUtilsWrapper httpUtilsWrapper
    ) {
        if (fileSystemUtilsWrapper == null 
            || httpUtilsWrapper == null) {
            throw new NullPointerException("...");
        }

        this.fileSystemUtilsWrapper = fileSystemUtilsWrapper;
        this.httpUtilsWrapper = httpUtilsWrapper;
    }

    public boolean doIOOperations(String filePath, String url) throws IOException {
        if (fileSystemUtilsWrapper.isMyFileReachable(filePath) 
            && httpUtilsWrapper.isMyURLReachable(url)) {
            // do something and returns IO state
        }
        return false;
    }
}

/**
 * FileSystem utils wrapper
 */
class FileSystemUtilsWrapper {
    public boolean isMyFileReachable(String filePath) {
        Path path = Paths.get(filePath);
        return Files.exists(path) && Files.isWritable(path);
    }
}

/**
 * http utils wrapper
 */
class HttpUtilsWrapper {
    public boolean isMyURLReachable(String url) throws IOException {
        HttpURLConnection urlConnection = (HttpURLConnection) new URL(url)
            .openConnection()
        ;
        // i keep it simple
        return urlConnection.getResponseCode() == 200;
    }
}

I just keep it simple for the sake of getting a quick insight.

Note that i've also considered using directly utility classes inside my SUT and mocking the I/O resources (creating and deleting a dummy file and using wiremock to mock the http endpoint ). While it may seem the easiest path to take, IMO one shouldn't do so, because it is not unit testing anymore but integration tests.

I would appreciate hearing your opinion on my analysis.
thanks in advance and Best Regards,

Best Answer

What I like here are the overridable default values. That's good. It avoids hard coding. I recommend it regardless of testing. And have done so before.

What I'm not sure I like is that IOManager uses the wrappers not only as default values but as types. Anything that means to replace the wrappers has to inherit from them. The wrappers are concrete, not abstract. Which means this is the start of a yo-yo problem.

A good principle that could guide you away from that problem is the Dependency Inversion Principle. It teaches that:

  • High-level modules should not depend on low-level modules. Both should depend on abstractions.
  • Abstractions should not depend on details. Details should depend on abstractions.

Which isn't to say you must use interfaces here. But it would be nice if at least there were abstract classes named FileSystemUtils and HttpUtils that IOManager can use as types. That way when I pick up your code and switch from new URL(url) to new SecureURL(url) by creating HttpsUtilsWrapper, I won't be forcing some poor maintenance coder to look at unused, overridden code.