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);
}
}
Related
I am trying to write Junit test cases for a void method. This method is used for updating values in Database. I have tried certain test cases and its returning a success. But when I check the coverage its showing zero. Can anyone tell me the proper way to write test cases for void methods.
this is my service class :
public class CustomerServiceImpl implements CustomerService {
#Autowired
ERepository eRepository;
#Autowired
ActivityUtil activityUtil;
#Override
public void updateCustomer(RequestDTO requestDTO)
throws CustomAException {
if (Objects.nonNull(requestDTO.getAdmissionId())) {
Optional<Admission> optionalAdmission = eRepository.findById(
requestDTO.getAdmissionId());
if (optionalAdmission .isPresent()) {
EAdmission eAdmission = optionalAdmission.get();
updateCustomer(requestDTO, eAdmission);
} else {
throw new CustomAException ("Admission details not found");
}
}
else {
throw new CustomAException ("Admission id not found");
}
}
private void updateCustomer(RequestDTO requestDTO,
EAdmission eAdmission)
throws CustomAException {
logger.info("updating customer info");
try {
if (ObjectUtils.isNotEmpty(eAdmission.getCustomer())) {
eAdmission.getCustomer().setCustomerEmailAddress(
requestDTO.getEmail());
eAdmission.getCustomer().setCorporateTelephoneNumber(
requestDTO.getCustomerPhone());
eAdmission.getCustomer().setZipCode(requestDTO.getZipCode());
eAdmission.getCustomer().setCustomerAddress1(requestDTO.getAddress1());
evfAdmissionRepository.save(evfAdmission);
activityUtil.createActivityLog(eAdmission, Constants.ENTRY_COMPLETED);
} else {
throw new CustomAException ("Customer details not found ");
}
} catch (Exception exception) {
logger.error(Constants.CUSTOMER_UPDATE_ERROR_MESSAGE);
throw new CustomAException (Constants.CUSTOMER_UPDATE_ERROR_MESSAGE);
}
I am trying to write test cases for updateCustomer but my test class has zero coverage even though its a success.
test class :
#SpringBootTest
public class CustomerServiceImplTest {
#InjectMocks
CustomerServiceImpl CustomerServiceImpl;
#Mock
ERepository eRepository ;
#Mock
ActivityUtil activityUtil;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void updateCustomerException() throws Exception {
CustomerServiceImpl CustomerServiceImplTest = mock(CustomerServiceImpl.class);
when(evfAdmissionRepository.findById(any())).thenThrow(ConstraintViolationException.class);
Mockito.doThrow(CustomAException .class).when(CustomerServiceImplTest).updateCustomer(setRequestDTO());
}
#Test
public void updateCustomerSuccess() throws Exception {
CustomerServiceImpl CustomerServiceImplTest = mock(CustomerServiceImpl.class);
CustomerServiceImplTest .updateCustomer(setRequestDTO());
//doNothing().when(evfCustomerServiceImpl).updateEVFCustomerOnSubscribe(any());
verify(CustomerServiceImplTest ).updateCustomerOn(setRequestDTO());
}
private RequestDTO setRequestDTO() {
RequestDTO eRequestDTO = new RequestDTO ();
eRequestDTO .setEmail("test");
// rest of code for setting value
return eRequestDTO ;
}
ArgumentCaptor can be used to capture and Assert Arguments in your method. you can read about ArgumentCaptor here
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.
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);
}
}
I have a class:
#Component
public class ContractorFormValidator implements Validator {
Logger logger = LoggerFactory.getLogger(ContractorFormValidator.class);
#Inject IBusinessDataValidator businessDataValidator;
#Override
public boolean supports(Class<?> clazz) {
return Contractor.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors) {
Contractor contractor = (Contractor) target;
if (!businessDataValidator.isNipValid(contractor.getContractorData().getNip())) {
errors.rejectValue("contractorData.nip", "invalid");
}
if (!businessDataValidator.isRegonValid(contractor.getContractorData().getRegon())) {
errors.rejectValue("contractorData.regon", "invalid");
}
}
}
How can I test it? I have tried this: How to test validation annotations of a class using JUnit? but this doesn't work cause the validate method in my validator requires Errors class passed to it's method signature.
I have no Idea if I can pass this Errors object to the validator. Is there any other way?
Have you tried to write a simple unit test for this?
#RunWith(SpringJUnit4ClassRunner.class)
public class ContractorFormValidatorTest {
#Autowired
private ContractorFormValidator validator;
#Test
public void testValidation() throws Exception {
Contractor contractor = new Contractor();
// Initialise the variables here.
Errors errors = new BeanPropertyBindingResult(contractor, "contractor");
validator.validate(contract, errors);
// If errors are expected.
Assert.assertTrue(errors.hasErrors());
for (Error fieldError : errors.getFieldErrors()) {
Assert.assertEquals("contractorData.nip", fieldError.getCode());
}
}
}
If you are going to use the validator in a controller implementation, then you need to use the MockMvc apis
Your set up can be included in the class above.
private MockMvc mockMvc
#Autowired
private MyController controller;
#Before
public void setUp() throws Exception {
this.mockMvc = MockMvcBuilders.standaloneSetup(this.controller).build();
}
#Test
public void testMethod() {
MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.post("/yoururl")).
andExpect(MockMvcResultMatchers.status().isCreated()).andReturn();
}
Use the class org.springframework.validation.BeanPropertyBindingResult,
Errors newErrors = new BeanPropertyBindingResult(validateObject, "objectName");
In my test when I assert de exception message I'm getting null
I'm not getting mock the message inside Service.... :(
I have:
My test:
#RunWith(MockitoJUnitRunner.class)
public class ServiceImplTest {
#InjectMocks
private ServiceImpl service;
#Mock
private Message message;
public static final String REQUIRED_FIELD = "required.field";
#Before
public void setUp() {
when(message.getMessage(eq(REQUIRED_FIELD), any(List.class))).thenReturn(REQUIRED_FIELD);
System.out.println(message.getMessage(REQUIRED_FIELD, new ArrayList()));
}
#Test(expected = MyException.class)
public void testCancel_shouldValidCancellation_and_ThrowTicketException_with_RequiredFieldMessage() {
try {
Object object = //... new object
service.do(object);
} catch (Exception e) {
assertEquals(REQUIRED_FIELD, e.getMessage()); // <-- e.getMessage return null!!!
}
}
}
My service:
#Service
public class ServiceImpl implements Service {
#Autowired
Message message;
#Override
public Object do(Object object) {
if(object.getSomeAttribute() == null)) {
throw new MyException(message.getMessage("required.field", "attribute"));
}
//doSomething...
return something;
}
}
In the setUp() of test the printLn() prints required.field but I can't use message of Service!!
Can someone help me?
It is hard tell for sure, without knowledge about the interface of Message, but it is easy to spot that you configure mock object to stub method with signature getMessage(String, List):
when(message.getMessage(eq(REQUIRED_FIELD), any(List.class))).thenReturn(REQUIRED_FIELD);
However, ServiceImpl uses getMessage(String, String). The default value which is returned by mock in this case is null. You have to configure mock object properly.