1

I've started developing a Java API which will consist of just a couple of public classes, a public interface (to be implemented by the user and used as a callback, like in the Observer pattern). All other classes are private and/or package-private. I want to use TDD. For all the reasons stated in other posts on this site, I want to write unit tests only for my public classes and methods.

My problem is that this API is going to be implemented, in its first version, using a REST client from a suitable third-party library to get data from a remote web app. In a future version, it might possibly use JDBC or a NoSQL facility to obtain the data from a remote database instead. The thing is, that's all private implementation detail that's not exposed to the user.

How do I mock the remote connections for unit testing? Ordinarily, I would use Dependency Injection. That is, I would pass a connection object (the dependency) to the public class, and that connection object would be either the real connection object or the mock one. The thing is, I don't want to complicate the API that is exposed to the user, just so that I can test its workings. I don't want the user to have to know there is such a thing as a "connection object" that needs to be passed to the public class. So I don't think I can use DI in my case. What can I do instead?

2
  • 1
    Why don’t you simply use DI but pass the Connection by default ? So one can optionally change the way it connects. It’s easy to test, simple to use and easy to change. Commented Jun 17, 2020 at 21:39
  • 1
    I don't want the user to have to know there is such a thing as a "connection object" that needs to be passed to the public class. Fine. Give a default implementation of the connector and leave open a window for the extension.
    – Laiv
    Commented Jun 18, 2020 at 6:58

3 Answers 3

1

If the private dependency cannot be changed by the consumer of your API, it belongs to the unit you want to test. Therefore, you should not mock it, as your test will be meaningless then.

So, basically two options:

  • Use DI, but make it public. Then you have an isolated unit that you can test, which depends on an external service. Yes, your interface will be a bit more complicated, but you can circumvent this by adding some utility methods (MyFactory.createStandardSetup() or so.)
  • Treat the service as part of the unit you want to test, mock the external parts. In the case of getting data from an external web service, have a look at wiremock (http://wiremock.org/). Further possibilities exist for many types of technologies, e.g. using an in-memory database for JDBC, etc.
4

What can I do instead?

Delegation.

Instead of having your externally visible class do the work you want to test, use a design where the externally visible class delegates the work to another component that is easy to test.

class ExternallyVisible {
    public void doSomethingCool(A a, B b, C c) {
        EasyToTest.doSomethingCool(privateImplementationDetail, a, b, c)
    }
}

EasyToTest uses whichever flavor of dependency injection is most appropriate for your situation, and all of the complicated work happens in there where you can test it in isolation.

ExternallyVisible is "so simple there are obviously no deficiencies". You "unit test" it by looking at it once, observing that it is indeed dead simple, and then leaving it alone.

If you must have automated tests for ExternallyVisible, they live much higher up the test pyramid than "unit tests".

The key idea is to create a design where the risk is contained within modules that are easy to test, and the "plumbing" that connects these modules is too simple to break.

Something worth noticing: ExternallyVisible is not only simple but stable - it just wires a few things together, so why would it ever need to change? It's small enough that the optimal change strategy might be "replace" rather than "modify". If ExternallyVisible isn't changing, then we don't need a big fleet of mistake detectors to evaluate changes to it.

0

Could you have the public class have a private method that accepts a connection object, and a public method that calls the private method with the default connection object?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.