Adding Code Coverage for catch block in Spring boot - java

Using 2.1.6.RELEASE
This is my serviceImpl class with repo.save method, in case of db field is duplicate we catch the exception and return in response
#Service
public class CoreVoucherServiceImpl implements CoreVoucherService {
#Override
#Transactional(propagation = REQUIRED)
public VoucherDTO createVoucher(VoucherDTO voucherDTO) {
... /* transforming DTO to Entity */
try {
voucherRepository.save(voucher);
} catch (Exception e) {
if (e.getCause() instanceof ConstraintViolationException) {
throw new MyException(FIELD_NOT_UNIQUE, "title");
}
UB_LOGGER.debug("Error in create voucher", e);
throw e;
}
voucherDTO.setId(voucher.getId());
return voucherDTO;
}
}
I am not able to add code coverage for the catch block. My Test class is
#SpringBootTest
#RunWith(SpringRunner.class)
public class CoreVoucherServiceTest {
#Autowired
private CoreVoucherService coreVoucherService;
#MockBean
private VoucherRepository voucherRepository;
#Test
// #Test(expected = MyException.class)
public void createVoucherTest() {
VoucherDTO dto = prepareCreateVoucher();
when(voucherRepository.save(any())).thenThrow(Exception.class);
coreVoucherService.createVoucher(dto);
}
}
with above way I am getting below error
org.mockito.exceptions.base.MockitoException:
Checked exception is invalid for this method!
Invalid: java.lang.Exception
How do I throw an Exception whose getCause is ConstraintViolationException so all lines are covered in testing

You have to test two use cases in your catch block:
When exception cause is ConstraintViolationException
.thenThrow(new RuntimeException(new ConstraintViolationException("Field not Unique", null, "title")));
When exception cause is not ConstraintViolationException
.thenThrow(new RuntimeException("oops"));
for this case #ExpectedException would be RuntimeException

You should throw the ConstraintViolationException, because save method does not throw any checked exception according to it's method definition save
when(voucherRepository.save(any()))
.thenThrow(.ConstraintViolationException.class);

You can also test exceptions with #Rule and ExpectedException in Junit.
#SpringBootTest
#RunWith(SpringRunner.class)
public class CoreVoucherServiceTest {
#Autowired
private CoreVoucherService coreVoucherService;
#MockBean
private VoucherRepository voucherRepository;
#Rule
public ExpectedException exceptionRule = ExpectedException.none();
#Test
// #Test(expected = MyException.class)
public void createVoucherTest() {
exceptionRule.expect(Exception.class); // Better if you specify specific Exception class that is going to be thrown.
VoucherDTO dto = prepareCreateVoucher();
when(voucherRepository.save(any())).thenThrow(Exception.class);
coreVoucherService.createVoucher(dto);
}
}

Related

Spring AOP AfterThrowing aspect doesn't work with mock objects in Junit 5

in my spring project I created simple aspect which catches DaoExceptions and turns them into service exceptions
#Aspect
#Component
public class ExceptionAspect {
#Pointcut("execution(public * *(..))")
private void allPublicMethods() {
}
#Pointcut("within(img.imaginary.dao.*)")
private void inDao() {
}
#AfterThrowing(pointcut = "allPublicMethods() && inDao()", throwing = "exception")
public void afterThrowing(JoinPoint joinPoint, DaoException exception) {
throw new ServiceException(String.format("%s in method %s %s class", exception.getMessage(),
joinPoint.getSignature().getName(), joinPoint.getTarget().getClass().getSimpleName()), exception);
}
}
and it works fine when DaoException throwing from dao layer to service it turns into Service exception
but only not in the tests:
#Test
void findById_ShouldThrowServiceException_WhenEntityNotFound() {
Mockito.when(defaultDao.findById(0)).thenThrow(DaoException.class);
assertThrows(ServiceException.class, () -> defaultServiceImpl.findById(0));
}
in this test I have a defaultDao and it is a Mock object and when it throws a DaoException my aspect does not catch and proxy it
I can't figure out how to solve this problem
With the following assumptions - it is a spring-boot project , defaultDao is a mocked bean using #MockBean
Do go through the issue to understand why #MockBean will not work.
Following code would mock a bean and throw an exception . You will need to adjust the code to make it work for you.( eg: #SpringBootTest may or may not be required for your case ).
#SpringBootTest
class DefaultDaoTest {
#Autowired
DefaultDao defaultDao;
#Test
void test() {
assertThrows(ServiceException.class, () -> defaultDao.findById(0));
}
}
#Configuration
class TestConfig {
#Bean
#Primary
public DefaultDao defaultDao() {
return new DefaultDao() {
#Override
public Object findById(Long id) {
throw new DaoException();
}
};
}
}

#ControllerAdvice not catch the thrown exceptions

I use java spring.
I try to use ControllerAdvice in my project as a global event handler:
#ControllerAdvice
public class ExceptionsHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(RestClientException.class)
public void RestClientException(RestClientException ex)
{
System.out.println("RestClientException" + ex.getMessage());
}
#ExceptionHandler
public void Exception(Exception ex)
{
System.out.println("Exception" + ex.getMessage());
}
}
In this function I create and throw an exception:
#Component
#RequiredArgsConstructor
public class Scheduler {
public void schedule() throws JsonProcessingException, Exception {
throw new Exception();
}
}
And:
#Component
#RequiredArgsConstructor
public class RestfulReaderServiceImpl implements RestfulReaderService {
public String getData(String url) throws RestClientException {
throw new RestClientException();
}
}
But when those functions are fired and exceptions are thrown the exception handlers
in class ExceptionsHandler does not execute.
Any idea why exception handlers in ExceptionsHandler class not catches and handles the
exceptions?
#ControllerAdvice only handles exceptions thrown from Controller methods i.e. methods annotated with #RequestMapping or any of its shortcut annotations (#GetMapping etc.). You can check this by calling your exception throwing method from any #RequestMapping annotated method.

Line is not covered according to test coverage, despite successfully using verify and asserts

My goal is for Sonar to detect that the error thrown in the MyService class (pointed through the comment) is actually covered in the test. However, Sonar says it is not. Despite successfully verifying that the logAndThrowException method is called, Sonar still says this line is not covered in the test.
MyService class:
public class MyService {
#Autowired
RestTemplate restTemplate;
#Autowired
MyExceptionHandler myExceptionHandler;
public ResponseEntity doSomeRequest() {
try {
ResponseEntity<String> response = restTemplate.exchange(requestEntity, String.class);
if (responseEntity.getStatusCode() == HttpStates.OK) {
return response;
}
myExceptionHandler.logAndThrowException("error"); // Sonar says this line is not covered in tests
} catch (RestClientException e) {
// Handle it
}
}
}
MyExceptionHandler class:
public class MyExceptionHandler {
public void logAndThrowException(String msg) throws MyCustomException {
// do some logs
throw new MyCustomException();
}
}
Test code:
#RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {
#Mock
private RestTemplate restTemplate
#Mock
MyExceptionHandler myExceptionHandler;
#Mock
ResponseEntity responseEntity;
#InjectMocks
MyService myService = new MyService();
#Test
public void testFailedRequest() {
when(restTemplate.exchange(any(RequestEntity.class), eq(String.class))).thenReturn(responseEntity);
when(responseEntity.getStatusCode()).thenReturn(HttpStatus.INTERNAL_SERVER_ERROR);
Mockito.doThrow(MyCustomException.class).when(myExceptionHandler).logAndThrowException(ArgumentMatchers.anyString());
assertThrows(MyCustomException.class, () -> myService.doSomeRequest());
Mockito.verify(myExceptionHandler, Mockito.times(1)).logAndThrowException(ArgumentMatchers.anyString());
}
}
I believe I've seen this before. The issue is that it doesn't "finish" executing that line, because an exception is thrown while executing that line.

Why doesn't my #Transactional method rollback when testing?

I have #transactional method that seems to be working (rolling back) if run the actual service and provide inputs that would cause a run-time error. If I create a Test for that method that throws a run-time error it doesn't seem to rollback anymore. Any idea why this doesn't work while testing?
it's somthing like:
#Service
public class SampleServiceImpl implements SampleService {
private final RepoA repoA;
private final RepoB repoB;
public SampleServiceImpl(RepoA repoA, RepoB repoB) {
this.repoA = repoA,
this.repoB = repoB
}
#Transactional
#Override
public void addItems() {
repoA.save(new ItemA(1,'name1')); //works
repoB.save(new ItemB(2,'name2')); //throws run-time error
}
}
#RunWith(SpringRunner.class)
#DataJpaTest
public class Tests {
#Autowired
private RepoA repoA;
#Mock
private Repob repoBMock;
#Test
public void whenExceptionOccurrs_thenRollsBack() {
var service = new SampleService(repoA, repoBMock);
Mockito.when(repoBMock.save(any(ItemB.class))).thenThrow(new RuntimeException());
boolean exceptionThrown = false;
try {
service.addItems()
} catch (Exception e) {
exceptionThrown = true;
}
Assert.assertTrue(exceptionThrown);
Assert.assertFalse(repoA.existsByName('name1')); // this assertion fails because the the first item still exists in db
}
}
Just add annotation Rollback and set the flag to false.
#Test
#Rollback(false)

Java - How to Test Catch Block?

Bit of a repost, but a certain catch-22 about not having enough reputation means I can't comment on any of the duplicate threads! (cough cough)
I'm trying to test the catch block of a try-catch using Mockito; is it possible to make a mock throw an exception that is handled by the method being tested? I can't use doThrow()...when()... or #Test(expected = Foo.class) because the exception is handled. I want to test that the method handles Exceptions properly.
#Controller
public class StockExchangeController {
public ModelAndView placeOrder(ModelAndView mav, MyObj myObj) {
try {
validator.validate(myObj); // Throws CustomException if validation fails
mav.setViewName("successPage");
} catch (CustomException ex) {
mav.setViewName("failPage");
}
return mav;
}
}
I'd like to be able to stub the behaviour of my "validatorObject", like
doThrow(new CustomException()).when(validatorMock).validate();
Is there a way to do this?
The answer here (Test catch block logic with Junit and mockito) doesn't work (I believe) because the exception is handled before it gets to test level.
Tips and thoughts much appreciated!
BDD Style Solution
Mockito alone is not the best solution for handling exceptions, use Mockito with Catch-Exception
Mockito + Catch-Exception + AssertJ
given(otherServiceMock.bar()).willThrow(new MyException());
when(myService).foo();
then(caughtException()).isInstanceOf(MyException.class);
Sample code
Mockito + Catch-Exception + Assertj full sample
Dependencies
eu.codearte.catch-exception:catch-exception:1.3.3
org.assertj:assertj-core:1.7.0
Disadvantage
Only Mockito up to 1.10.8 is supported
Why should doThrow(..).when(..)... not work?
The placeOrder method catches the exception and returns a testable result:
#RunWith(MockitoJUnitRunner.class)
public class TestStockExchangeController {
#Mock
Validator validator;
#Mock MyObj myObj;
#Test
public void testException() throws CustomException {
StockExchangeController sec = new StockExchangeController(validator);
doThrow(new CustomException()).when(validator).validate(myObj);
ModelAndView modelAndView = sec.placeOrder(new ModelAndView(), myObj);
assertEquals("failPage", modelAndView.getViewName());
}
}
I've tested with these two files:
Main source code:
//src/main/java/StockExchangeController.java
public class StockExchangeController {
private ValidationFactory factory;
public ModelAndView placeOrder(ModelAndView mav, MyObj myObj) {
Validator validator = factory.getValidator("S");
try {
validator.validate(myObj); // Throws CustomException if validation fails
mav.setViewName("successPage");
} catch (CustomException ex) {
mav.setViewName("failPage");
}
return mav;
}
}
class CustomException extends Exception {}
interface ValidationFactory {
Validator getValidator(String s);
}
class Validator {
public void validate(MyObj myObj) throws CustomException {}
}
class ModelAndView {
private String viewName;
public void setViewName(String viewName) {
this.viewName = viewName;
}
public String getViewName() {
return viewName;
}
}
class MyObj {}
Test source code:
//src/test/java/TestStockExchangeController.java
//various imports
#RunWith(MockitoJUnitRunner.class)
public class TestStockExchangeController {
#Mock
private Validator validator;
#Mock
private ValidationFactory validationFactory;
#InjectMocks
StockExchangeController target = new StockExchangeController();
#Test
public void testException() throws CustomException {
MyObj myObj = new MyObj();
when(validationFactory.getValidator(anyString())).thenReturn(validator);
doThrow(new CustomException()).when(validator).validate(myObj);
ModelAndView modelAndView = target.placeOrder(new ModelAndView(), myObj);
assertEquals("failPage", modelAndView.getViewName());
verify(validator).validate(myObj);
}
}

Categories

Resources