while trying to create some unit tests for my code, I've started to see some implicit dependencies which make it really difficult to make my code testable in isolation. I've started to look into how to refactor that code, but it's giving me a hard time to come up with a design that feels good.
The goal of the class is to provide a number of methods related by domain. These methods return values which can be produced in a remote client or in a local client, and there needs to be logic to decide which client provides the value. The way the clients are created cannot be changed, as they are external dependencies (aka, not my code). Finally, there is some configuration holder class which is injected via IoC container.
The problem is not the IoC part, but the other two dependencies.
Code looks something like this:
class ServiceImpl {
@Inject
private Config config;
private RemoteClient remote;
private LocalClient local;
public boolean initializeService() {
property1 = config.specificConfig.property1;
property2 = config.specificConfig.property2;
if (remote == null || property1 != remote.property1) {
remote = StaticFactory.buildClient(property1, property2);
}
try {
if (local == null) {
local = new LocalClient(property1, property2);
}
} catch {
return false;
}
return remote != null && local != null;
}
public int method() {
if (initializeService()) {
value = local.getValue();
if (value == null) {
value = remote.getAnotherValue();
local.setValue(value);
}
} else {
value = -1;
}
return value;
}
}
The initialize() method cannot go into the constructor, as the class is instantiated once. However, its purpose is still needed, since the two dependencies are built based on the configuration, which can change during runtime, triggering a new reinstantiation of the dependencies.
I looked into the possibility of adding a new layer which could be injected via IoC, which would be no more than an interface hiding the building details of RemoteClient and LocalClient. That way, in my test, I could mock that additional layer and return dummy clients. However, it feels overdone and much along the lines of a bad use of service locator pattern, and the fact that I would have a mock returning mocks seems to back that feeling up.
Another alternative would be extracting the building of RemoteClient and LocalClient, but that would make also things more complicated, because then I would need some other process watching over configuration changes, in order to reinstantiate the dependent clients.
I do realize that this class is doing too much, as it is responsible to create the clients and to process the calls to the clients, and the fact that I cannot unit test it well tells me that I should reconsider the design, but I'm not sure what would be the right way to refactor it.
Any ideas about it?
Aucun commentaire:
Enregistrer un commentaire