How to perform JUnit test on Custom Exception - java

I have a Custom exception that handles the expected errors of my program and here is the code.
#ControllerAdvice
#RestController
public class DashboardException {
#ExceptionHandler({Exception.class, IOException.class, ParseException.class, JsonProcessingException.class})
public final ResponseEntity<ErrorDetails> dataNotFoundException(Exception ex, WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails();
errorDetails.setTimestamp(new Date().toString());
errorDetails.setMessage(ex.getMessage());
errorDetails.setPath(request.getDescription(false));
errorDetails.setStatus(HttpStatus.BAD_REQUEST.value());
errorDetails.setError(HttpStatus.BAD_REQUEST);
return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST);
}
}
My problem is on how to properly unit test this class. This is what I have made so far to make it cover, but with no luck.
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(classes = { DashboardException.class, TestConfiguration.class, DataController.class })
public class testDashboardException {
private MockMvc mockMvc;
#Autowired
WebApplicationContext wac;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
#Configuration
#EnableWebMvc
public static class TestConfiguration { }
#Controller
#RequestMapping("/tests")
public static class RestProcessingExceptionThrowingController {
#GetMapping(value = "/exception")
public #ResponseBody String find() throws Exception {
throw new Exception("global_error_test");
}
}
#Test
public void testHandleException() throws Exception {
mockMvc.perform(get("/tests/exception"))
.andExpect(new ResultMatcher() {
#Override
public void match(MvcResult result) throws Exception {
result.getResponse().getContentAsString().contains("global_error_test");
}
})
.andExpect(status().isBadRequest());
}
/*
* #Test public void testErrorDetailsValue() {
*
* thrown.expect(Exception.class); thrown.expect(IOException.class);
* thrown.expect(ParseException.class);
* thrown.expect(JsonProcessingException.class);
*
* thrown.expectMessage("Bad Request");
*
* }
*/
}
I only have a little knowledge concerning custom exceptions. What am I missing here? Thanks for any assistance.

I found out how to cover my custom exception. I just included a test on my controller that will fail the endpoint and it did catch an exception and covered my custom exception.

Related

No mapping for request with mockmvc

Currently struggling with problem when I get 'mapping error for request' with following controller/test configuration.
Controller:
#Slf4j
#Validated
#RestController
#RequiredArgsConstructor
public class AdtechController {
private final AdtechService adtechService;
#PostMapping(value = "/subscriber/session")
public ResponseEntity<ResponseDto> submitSession(#RequestBody RequestDto requestDto) {
log.trace("execute submitSession with {}", requestDto);
ResponseDtoresponse = adtechService.submitSession(requestDto);
return new ResponseEntity<>(response, HttpStatus.OK);
}
#ExceptionHandler(AdtechServiceException.class)
public ResponseEntity<AdtechErrorResponse> handleAdtechServiceException(AdtechServiceException e) {
return new ResponseEntity<>(new AdtechErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Test:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
#SpringJUnitConfig({AdtechTestConfig.class})
public class AdtechControllerTest {
private static final ObjectMapper OBJECT_MAPPER = JsonUtil.getJackson();
#Autowired
private MockMvc mockMvc;
#Test
public void testSubmitSession() throws Exception {
RequestDto requestDto = new RequestDto ();
requestDto.setKyivstarId("1123134");
requestDto.setMsisdn("123476345242");
requestDto.setPartnerId("112432523");
requestDto.setPartnerName("125798756");
String request = OBJECT_MAPPER.writeValueAsString(requestDto);
System.out.println("REQUEST: " + request);
String response = OBJECT_MAPPER.writeValueAsString(new ResponseDto("123"));
System.out.println("RESPONSE: " + response);
mockMvc.perform(post("/subscriber/session")
.content(MediaType.APPLICATION_JSON_VALUE)
.content(request))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().string(containsString(response)));
}
}
Configuration:
#Configuration
public class AdtechTestConfig {
#Bean
public AdtechService adtechTestService() {
return requestDto -> new AdtechResponseDto("123");
}
}
After test execution I get No mapping for POST /subscriber/session
The reason for the struggle is that my code from other modules with the same configuration works fine. Can somebody point out what am I missing ? Thanks in advance!
Apparently you are loading a configuration class to mock beans, this interferes with the other parts of Spring Boot and probably leads to partially loading your application. I suspect only the mocked service is available.
Instead of the test configuration use #MockBean to create a mock for the service and register behaviour on it.
#SpringBootTest
#AutoConfigureMockMvc
public class AdtechControllerTest {
private static final ObjectMapper OBJECT_MAPPER = JsonUtil.getJackson();
#Autowired
private MockMvc mockMvc;
#MockBean
private AdtechService mockService;
#BeforeEach
public void setUp() {
when(mockService.yourMethod(any()).thenReturn(new AdtechResponseDto("123"));
}
#Test
public void testSubmitSession() throws Exception {
// Your original test method
}
}
If the only thing you want to test is your controller you might also want to consider using #WebMvcTest instead of #SpringBootTest.
#WebMvcTest(AdTechController.class)
public class AdtechControllerTest {
private static final ObjectMapper OBJECT_MAPPER = JsonUtil.getJackson();
#Autowired
private MockMvc mockMvc;
#MockBean
private AdtechService mockService;
#BeforeEach
public void setUp() {
when(mockService.yourMethod(any()).thenReturn(new AdtechResponseDto("123"));
}
#Test
public void testSubmitSession() throws Exception {
// Your original test method
}
}
This will load a scaled-down version of the context (only the web parts) and will be quicker to run.
try this:
#Slf4j
#Validated
#RestController
#RequiredArgsConstructor
public class AdtechController {
private AdtechService adtechService;
public AdtechController (AdtechService adtechService) {
this.adtechService= adtechService;
}
#PostMapping(value = "/subscriber/session")
public ResponseEntity<ResponseDto> submitSession(#RequestBody RequestDto requestDto) {
log.trace("execute submitSession with {}", requestDto);
ResponseDtoresponse = adtechService.submitSession(requestDto);
return new ResponseEntity<>(response, HttpStatus.OK);
}
#ExceptionHandler(AdtechServiceException.class)
public ResponseEntity<AdtechErrorResponse> handleAdtechServiceException(AdtechServiceException e) {
return new ResponseEntity<>(new AdtechErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Test:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
#SpringJUnitConfig({AdtechTestConfig.class})
public class AdtechControllerTest {
private static final ObjectMapper OBJECT_MAPPER = JsonUtil.getJackson();
#Autowired
private MockMvc mockMvc;
#Autowired
private AdtechService adtechService;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
this.mvc = MockMvcBuilders.standaloneSetup(new AdtechController(adtechService)).build();
}
#Test
public void testSubmitSession() throws Exception {
RequestDto requestDto = new RequestDto ();
requestDto.setKyivstarId("1123134");
requestDto.setMsisdn("123476345242");
requestDto.setPartnerId("112432523");
requestDto.setPartnerName("125798756");
String request = OBJECT_MAPPER.writeValueAsString(requestDto);
System.out.println("REQUEST: " + request);
String response = OBJECT_MAPPER.writeValueAsString(new ResponseDto("123"));
System.out.println("RESPONSE: " + response);
mockMvc.perform(post("/subscriber/session")
.content(MediaType.APPLICATION_JSON_VALUE)
.content(request))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().string(containsString(response)));
}
}
Is the AdtechTestConfig.class introducing the /ad-tech path segment in to your test request? If so, this is why your test is trying the path /ad-tech/subscriber/session instead of /subscriber/session.
If this is actually the correct uri, then you may add #RequestMapping to the controller like below or just to the post method itself
#Slf4j
#Validated
#RestController
#RequestMapping("/ad-tech")
#RequiredArgsConstructor
public class AdtechController {
private final AdtechService adtechService;
#PostMapping(value = "/subscriber/session")
public ResponseEntity<ResponseDto> submitSession(#RequestBody RequestDto requestDto) {
...

MockMvc and #ControllerAdvice doesn't work in tests

I'm testing my RestController with mockMvc. I have a global RestExceptionHandler to resolve all exceptions. In my RestController I throw custom Exception RequestValidationException like this:
#ApiOperation("Search something")
#RequestMapping(path = "/search", method = RequestMethod.POST)
public CompletableFuture<SomeResponse> search(
#RequestBody #Validated SearchRequest request, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
throw new RequestValidationException(bindingResult);
}
return searchService.search(request);
}
And when i pass empty request it must throw RequestValidationException(bindingResult)
but when i start tests they fall in that place where i throw Exception instead to resolve it.
i try to configure my mockMvc like this:
#RunWith(SpringRunner.class)
public class SearchControllerTest {
private MockMvc mockMvc;
#InjectMocks
protected SearchController searchController;
#MockBean
private SearchService searchService;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(searchController)
.setHandlerExceptionResolvers(getHandlerExceptionResolver())
.build();
}
private HandlerExceptionResolver getHandlerExceptionResolver() {
final StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerSingleton("exceptionHandler", RestExceptionHandler.class);
final WebMvcConfigurationSupport webMvcConfigurationSupport = new WebMvcConfigurationSupport();
webMvcConfigurationSupport.setApplicationContext(applicationContext);
return webMvcConfigurationSupport.handlerExceptionResolver();
}
but it doesnt help. i'm getting an Exception insted json with message.
My RequestValidationExceptionHandler:
#Component
public class RequestValidationExceptionHandler implements ApiExceptionHandler {
#Override
public ResponseEntity<ApiResponse> process(Throwable throwable) {
RequestValidationException e = (RequestValidationException) throwable;
if (e.getBindingResult() != null) {
return new ResponseEntity<>(ApiResponse.badRequest(e.getBindingResult()), HttpStatus.OK);
}
return new ResponseEntity<>(ApiResponse.badRequest(throwable, ApiResponseCode.BAD_REQUEST), HttpStatus.OK);
}
#Override
public Class<? extends Throwable> getSupportedException() {
return RequestValidationException.class;
}
}
2) My #ControllerAdvice:
#Slf4j
#ControllerAdvice
#SuppressWarnings({"checkstyle:JavadocMethod", "checkstyle:MultipleStringLiterals"})
public class RestExceptionHandler {
#Autowired
private ExceptionHandlerRegistry handlerRegistry;
#ExceptionHandler
public ResponseEntity handleThrowable(Throwable throwable, WebRequest request) {
request.setAttribute(Constants.ERROR_ATTRIBUTE_NAME, throwable, RequestAttributes.SCOPE_REQUEST);
Throwable ex = throwable instanceof CompletionException ?
ObjectUtils.defaultIfNull(throwable.getCause(), throwable) : throwable;
for (ApiExceptionHandler handler : handlerRegistry.getHandlers()) {
if (handler.isSupported(ex)) {
return handler.process(ex);
}
}
return new ResponseEntity<>(ApiResponse.badRequest(throwable, ApiResponseCode.SERVER_ERROR), HttpStatus.OK);
}
}
3) And ExceptionHandlerRegistry :
#Component
public class ExceptionHandlerRegistry {
#Getter
private final List<ApiExceptionHandler> handlers;
#Autowired
public ExceptionHandlerRegistry(List<ApiExceptionHandler> handlers) {
this.handlers = ObjectUtils.defaultIfNull(handlers, Collections.emptyList());
}
}
The Error message:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is ru.filit.mvideo.mb2c.api.exceptions.RequestValidationException
UPDATE
So after some discussion with #MichaelMichailidis, i try to do this, i just add an inner #Configuration class with needed beans:
#TestConfiguration
static class SearchControllerTestConfiguration {
#Bean
public RequestValidationExceptionHandler requestValidationExceptionHandler(){
return new RequestValidationExceptionHandler();
}
#Bean
public ExceptionHandlerRegistry getExceptionHandlerRegistry(final RequestValidationExceptionHandler requestValidationExceptionHandler){
return new ExceptionHandlerRegistry(Collections.singletonList(requestValidationExceptionHandler));
}
#Bean
public RestExceptionHandler getRestExceptionHandler(){
return new RestExceptionHandler();
}
}
and my test pass. But i can't understand why test were working without configuration before i add #ControllerAdvice?
You can try importing your exception handler in your test class:
#RunWith(SpringRunner.class)
#Import(RestExceptionHandler.class) // EXCEPTION HANDLER CLASS
public class SearchControllerTest {

#Pointcut for all server request doesn't work with test context

I try some variant with #pointcut to intercept all request to server, but they doesn't work with test context.
My examples
#Pointcut("execution (public * example.server.web.rest..*(..))")
#Pointcut("within(example.server.web.rest..*)")
#Pointcut("within(#org.springframework.web.bind.annotation.RestController *)")
I just want catch all request with test context. How to set the necessary context?
Updated
#Aspect
public class UsersAspect {
#Pointcut("execution (public * example.server.web.rest..*(..))")
public void allServerRequests() {
}
#Around(value = "allServerRequests()")
public Object allServerRequest(final ProceedingJoinPoint pjp) throws Throwable {
//some code
return pjp.proceed();
}
}
configuration class
#Configuration
#EnableAspectJAutoProxy(proxyTargetClass = true)
public class UserAspectConfiguration {
private final UserService userService;
public UserAspectConfiguration(UserService userService) {
this.userService = userService;
}
#Bean
public UsersAspect userAspect(Environment env) {
return new UsersAspect(userService, env);
}
}
Example of test
#Test
#Transactional
public void checkAllServerRequestAspect() throws Exception {
UserDTO userDTO = new UserDTO();
userDTO.setSomething()
userService.updateUser(userDTO);
restUserMockMvc.perform(get("/api/any-rout")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isForbidden());
}
Now i can catch my test request only with like this pointcut
#Pointcut("execution (public * example.server.service.UserService.*(..))")
But #pointcut for all server request not fired with test context, or something else(

How can I test a validator in a Spring Boot app?

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");

How to use an interface property in my RestController to return data in my request handlers

I have declared an interface as a property of my #RestController. That interface just has a few fields/setters/getters.
The implementation of the RestController and GetMapping look like this:
#RestController
#EnableAutoConfiguration
public class AccountController {
private AccountStoreInterface store;
#GetMapping(value="/account")
public Account readAccount(#RequestParam("id") String id) throws AccountNotFoundException {
Account a = store.getAccount(id);
if (a.getId().isEmpty()) {
throw new AccountNotFoundException();
}
return a;
}
#ExceptionHandler(AccountNotFoundException.class)
#ResponseStatus(NOT_FOUND)
public #ResponseBody String handleAccountNotFoundException(AccountNotFoundException ex) {
return ex.getMessage(); }
#ExceptionHandler(NullPointerException.class)
#ResponseStatus(INTERNAL_SERVER_ERROR)
public #ResponseBody String handleNullPointerException(NullPointerException ex) {
return ex.getMessage();
}
}
The interface declaration looks like this:
public interface AccountStoreInterface {
public Account getAccount(String id) throws AccountNotFoundException;
public Account setAccount(String id, Account account) throws AccountConflictException;
}
I would like to test this using spring-boot-starter-test and junit4. I expect the following test to return a 500 because I have not passed any store object that implements my interface so it should throw a NullPointerException.
How do I configure my unit tests in order to test the 500 and 404 status codes?
Right now, the below test actually fails because the returned status is 200, which I don't understand how junit gets to.
#RunWith(SpringRunner.class)
#WebMvcTest(AccountController.class)
public class TestAccountController {
#Autowired private MockMvc mockMvc;
#Autowired private WebApplicationContext wac;
#MockBean
private AccountController accountController;
#Test
public void testGetAccountNotFound() throws Exception {
mockMvc.perform(get("/account?id={id}", "test-account-id-123")
.accept(APPLICATION_JSON)
.characterEncoding("UTF-8"))
.andDo(print())
.andExpect(status().isNotFound());
}
}

Categories

Resources