I have a class where I am using transactional template and I wrote a Junit test for the same. However when I run the coverage,I do not have any line coverage on the code. The test is although green. This confuses me. Can somebody be kind enough to help me resolve this. Below is my production code.
@Test
void test() {
Optional<SomeParam> someParam = Optional.empty();
String TOPIC = "topic";
Token token = getToken();
Stream<Token> tokenStream = Stream.of(token);
TokenDto tokenDto = getDto();
when(specificDataMapper.fromToken(token)).thenReturn(tokenDto);
doAnswer(invocation -> {
TransactionCallbackWithoutResult callback = new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
Stream<Token> stream = tokenRepository.streamAll();
stream.forEach(t -> {
TokenDto payload = specificDataMapper.fromToken(t);
pubSubTemplate.publish(TOPIC, payload);
});
}
};
callback.doInTransaction(null);
return null;
}).when(transactionTemplate).executeWithoutResult(any());
doReturn(tokenStream).when(tokenRepository).streamAll();
publisherService.publishservice(someParam);
}
When I run in debug, the test is not entering the lamda function.
The stubbed methods of your mock instances in your test never call your real method:
doAnswer(invocation -> {
TransactionCallbackWithoutResult callback = new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
Stream<Token> stream = tokenRepository.streamAll();
stream.forEach(t -> {
TokenDto payload = specificDataMapper.fromToken(t);
pubSubTemplate.publish(TOPIC, payload);
});
}
};
callback.doInTransaction(null);
return null;
}).when(transactionTemplate).executeWithoutResult(any());
It simply creates a new TransactionCallbackWithoutResult
instance and uses that to execute the code in the test (which seems to be copied verbatim from your production code's publishMessage
method?)
You probably want to call your real method. While this is possible to do in the doAnswer
block, it becomes convoluted and unmaintainable pretty quickly. Instead, I'd suggest to create a simple fake implementation of TransactionTemplate
to be used in your test. A trivial implementation simply calls the callback, without performing any transaction handling:
private static class FakeTransactionTemplate extends TransactionTemplate {
@Override
public <T> T execute(final TransactionTemplate<T> action) throws TransactionException {
return action.doInTransaction(new SimpleTransactionStatus());
}
}
Remove your Mockito mock and inject an instance of the fake transaction template into your service.
If you still want to use Mockito to re-implement the logic, you need to call the real template in your stub:
doAnswer(invocation -> {
final TransactionCallback<?> callback = invocation.getArgument(0)
callback.doInTransaction(new SimpleTransactionStatus()); // call the real callback!
return null;
}).when(transactionTemplate).executeWithoutResult(any());
But I'd advise against that. It gives you nothing over the fake implementation and only makes reasoning about your code harder.