Say I have a following class:
public class A {
public void execute() {
if (something)
ThirdPartyApi.method();
}
}
Now, I would like to test in particular the execute()
method. I want to assure that all paths are covered and work as expected. However, the issue I would run into here, is not being able to mock the ThirdPartyApi
because it's not provided as a dependency. An easy fix to this is to provide the ThirdPartyApi
as a dependency like this.
public class A {
private ThirdPartyApi api;
public A(ThirdPartyApi api) {
this.api = api;
}
public void execute() {
if (something)
api.method();
}
}
Now I would get the functionality I want and everyone would be happy. However, what I don't like here is the fact that I need to provide all internal dependencies through a constructor/set methods in order to make my class fully testable and independent of any external dependencies (Android API, libraries, utils etc.).
This becomes a problem for dependencies that are not directly needed for accomplishing the use case of the class. They are a mere tool e.g unit conversion tool. This way I request from the user of this class to provide me a dependency that I can internally create on my own. It makes the API harder to use, because the user must know more about the class than they should/need to.
To make the example more specific, say the ThidPartyApi
is a huge external unit conversion library. Why would a user need to provide me a class from this library through a constructor? It's something that's a mere side tool used to accomplish the use case. It's not directly related to the use case itself. Requiring this dependency in a constructor would make the API more confusing. See following class:
public class Storage {
private ConversionTool api;
public Storage (ConversionTool api) {
this.api = api;
}
public void storeInteger(int a) {
storeString(api.convertToString(a));
}
}
vs
public class Storage {
private ConversionTool api;
public Storage () {
this.api = new ConversionTool();
}
public void storeInteger(int a) {
storeString(api.convertToString(a));
}
}
What would be the preferred approach? Providing side dependencies through the constructor makes it more testable and mockable, but the API loses on its meaning. Not providing side dependencies through the constructor keeps the meaning to a class, but I lose the ability to mock everything inside the class and fully test the class.