I'm working on a service which has a lots of dependencies. The way how I call them is I'm wrapping each service client around an Adapter
. Like this (I'm using Java):
public abstract class AdapterBase<Request, Response> {
protected abstract String adapterMetadata();
protected abstract Response makeActualCall(Request request);
// additional things are happening here, for each dependency call it's the same
public Optional<Response> call(Request request) {
try {
return Optional.ofNullable(makeCall(request));
} catch(Exception ex) {
log.error("bad thing happened", ex);
return Optional.empty();
}
}
}
Now, for each API I'm calling, I'm creating a new adapter, and for each adapter, I'm creating a new unit test.
A sample implementation of this AdapterBase
:
public class FooAdapter<FooRequest, FooResponse> extends AdapterBase<FooRequest, FooResponse> {
private FooClient fooClient;
public FooAdapter(FooClient fooClient) {
this.fooClient = fooClient;
}
@Override
protected String adapterMetadata() {
"Calling Foo";
}
@Override
protected FooResponse makeActualCall(FooRequest fooRequest) {
return fooClient.call(fooRequest);
}
}
And I have the similar inherited classes for each API. The basic unit test for this concrete FooAdapter
would be the following:
@RunWith(MockitoJUnitRunner.class)
public class FooAdapterTest {
@Mock
private FooClient fooClient;
@Mock
private FooResponse fooResponse;
private FooAdapter fooAdapter;
@Before
public void setup() {
fooAdapter = new FooAdapter(fooClient);
}
@Test
public void whenExceptionIsThrownThenEmptyObservableReturns() {
when(fooClient.call(any()).thenThrow(FooException.class);
Optional<FooResponse> actual = fooAdapter.call(new FooRequest());
assertFalse(actual.isPresent());
}
@Test
public void whenCallSucceededThenResultReturns() {
when(fooClient.call(any()).thenReturn(fooResponse);
Optional<FooResponse> actual = fooAdapter.call(new FooRequest());
assertTrue(actual.isPresent());
assertEquals(fooResponse, actual.get());
}
}
And for all the concrete adapters, I have the same unit tests. At this point, I'm wondering if these unit tests are holding any value to us. It's all the same, the only difference is that different "clients" can throw different exceptions, but because of the implementation of AdapterBase
, it doesn't really matter what exception it was.
Having these classes are obviously increasing test-coverage metric, but I'm not 100% convinced on what value we are getting out of it. I'm thinking of changing this to one artificial implementation of the AdapterBase
and I only have one single unit test against it, since all the adapters are following the same pattern.
I was reading that too many very low-level unit tests might be an obstacle against agile changes since it requires lots of code changes in non-production-related codebase. However, each adapter is a single unit, therefore it has to be tested.
I'm curious about what would be the right way to follow in this case.