Spring boot mapping exception cannot test - spring-boot

I'm trying to test this basic controller class and can’t seem to test the exceptions being mocked in the test. It just doesn’t seem to catch the exception, just returns 200 ok
Update:
I have the exception now being thrown, but the controller advice is not catching them.
Controller
private final Service service;
#GetMapping(value = “/cars/{id}”, produces = APPLICATION_JSON_VALUE)
public ResponseEntity<List<Cars>> getCar(#PathVariable final String carId) {
var car = this.service.getCar(carId);
return ResponseEntity.ok(car);
}
Here is my mapping class
#RestControllerAdvice
public class Mapper {
#ExceptionHandler({CarNotFoundException.class})
public ResponseEntity<Object> notFoundError(final CarNotFoundException ex, final ServletWebRequest request) {
return ResponseEntity.status(NOT_FOUND).contentType(MediaType.APPLICATION_JSON).body(“test”);
}
}
Exception class:
public class CarNotFoundException extends RuntimeException {
public CarNotFoundException(final String msg, final Exception ex) {
super(msg, ex);
}
public CarNotFoundException(final String msg) {
super(msg);
}
}
My Test:
It just keeps returning 200
#WebMvcTest(MyController.class)
#ContextConfiguration(classes={MyApplication.class})
public class ControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private Service service;
#Test
void should_return_404_not_found() throws Exception {
when(this.service.getCar(CONSTANT.CAR_ID))
.thenThrow(new CustomNotFoundException("not Found"));
mockMvc.perform(get("/api/cars/98776")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isNotFound());
}
}

Related

How to write Junit test cases for this class

#DeleteMapping(value = "/{id}", produces = APPLICATION_JSON_VALUE)
public ResponseEntity<Void> delete(#PathVariable Long id) {
log.debug("Delete by id Logo : {}", id);
try {
Logo entr = new Logo();
entr.setId(id);
logoRepository.delete(entr);
return ResponseEntity.ok().build();
} catch (Exception x) {
return ResponseEntity.status(HttpStatus.CONFLICT).build();
}
}
For testing controllers we could mock mvc. Inject mock of service layers inside the controller.
Since you have mock for your service you could mock the response that service should return for a transaction using Mockito.when statement.
A sample code will be like.
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = HelloController.class)
public class HelloWorldTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private HelloService service;
#Test
public void testShouldReturnMessage() throws Exception {
when(service.sayHello()).thenReturn("Hello World");
mockMvc.perform(MockMvcRequestBuilders.get("/hello").accept(MediaType.ALL))
.andExpect(MockMvcResultMatchers.status().is(200))
.andExpect(MockMvcResultMatchers.content().string("Hello World"))
.andExpect(MockMvcResultMatchers.header().string("Content-Type", "text/plain;charset=UTF-8"))
.andExpect(MockMvcResultMatchers.header().string("Content-Length", "11"));
}
}
Reference - https://www.nexsoftsys.com/articles/spring-boot-controller-unit-testing.html#:~:text=Unit%20testing%20Spring%20Boot%20controllers%20Technology%20Unit%20testing,is%20to%20validate%20each%20unit%20performs%20as%20designed.

NullPointerException thrown during junit test without #SpringBootTest annotation, but not with the annotation

When I remove the #SpringBootTest annotation, I get a NullPointerException during this test:
#SpringBootTest
#RunWith(MockitoJUnitRunner.class)
public class ExceptionInterceptorTests {
private AysUserProvisException aysUserProvisException =
new AysUserProvisException("Failed","True", "Failed to create user. User already exists.", null);
#InjectMocks #Spy ExceptionInterceptor exceptionInterceptorSpy;
#Test
void testAysUserProvisException_generateCorrectResponseSchema() {
ResponseEntity<Object> response = exceptionInterceptorSpy.handleAysUserProvisException(aysUserProvisException);
AysUserProvisResponse exceptionResponse =
new AysUserProvisResponse(
"Failed", "True", "Failed to create user. User already exists.", null);
ResponseEntity<Object> expected =
new ResponseEntity<>(exceptionResponse, HttpStatus.INTERNAL_SERVER_ERROR);
assertEquals(response.getBody(), expected.getBody());
}
It is thrown when attempting to execute this method:
#ControllerAdvice
public class ExceptionInterceptor extends ResponseEntityExceptionHandler {
#ExceptionHandler(AysUserProvisException.class)
public final ResponseEntity<Object> handleAysUserProvisException(AysUserProvisException ex) {
AysUserProvisResponse exceptionResponse =
new AysUserProvisResponse(
ex.getStatus(), ex.getIsErrorOccurred(), ex.getMessage(), ex.getError());
return new ResponseEntity<>(exceptionResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
Here is the AysUserProvisResponse class:
public class AysUserProvisResponse {
private String status;
private String isErrorOccurred;
private String message;
private Error error = new Error();
public AysUserProvisResponse() {
super();
}
public AysUserProvisResponse(String status, String isErrorOccurred, String message, Error error) {
super();
this.status = status;
this.isErrorOccurred = isErrorOccurred;
this.message = message;
this.error = error;
}
How does the #SpringBootTest annotation avoid this exception? Why is it necessary?
You have to upload the spring context if in your code you are using the annotations or trying to inject beans.
Try to mock the dependencies of your Exception class and inject mocks into ExceptionInterceptor class

Unit testing Camel/RabbitMQ routes issue

I'm having issue unit testing a camel route which uses rabbitmq for the broker.
I've been researching for weeks but haven't found an effective way to do this.
Firstly, I was having an issue with NOT calling rabbitmq in my test, and to keep this a unit test and not an integration test. This was achieved by using advicewith and switch out the queue for mock queues.
However, with the following code the messages are not reaching the result or end queue (MOBILE_QUEUE).
java.lang.AssertionError: mock://result Received message count. Expected: <1> but was: <0>
Expected :<1>
Actual :<0>
Here is my route, which imports rabbitmq.class
from(TEST_QUEUE).to(MOBILE_QUEUE).routeId("test2phone");
My config rabbitmq.class
#Component
public class RabbitMQ extends Properties {
public final String TEST_QUEUE = CreateRabbitMQQueue("TestQueue", "camel");
public final String MOBILE_QUEUE = CreateRabbitMQQueue("MobileQueue", "camel");
public static String CreateRabbitMQQueue(String QueueName, String RoutingKey)
{
String hostv;
String portv;
String username;
String password;
hostv = "mq-staging";
portv = System.getenv("SERVICE_PORT_AMQP");
username = System.getenv("V_RABBIT_USERNAME");
password = System.getenv("V_RABBIT_PASSWORD");
UriComponentsBuilder uriBuilder = UriComponentsBuilder
.fromPath("/" )
.scheme("rabbitmq")
.host(hostv)
.port(portv)
.path("/" + QueueName)
.queryParam("username",username)
.queryParam("password", password)
.queryParam("routingKey",RoutingKey)
.queryParam("queue","Q" + QueueName);
return uriBuilder.toUriString();
}
}
And my unit test
#RunWith(CamelSpringRunner.class)
#MockEndpoints
#UseAdviceWith
#SpringBootTest
public class RouteTester extends CamelTestSupport {
String TEST_QUEUE;
String MOBILE_QUEUE;
#Autowired
Routes routes;
#Autowired
CamelContext context;
#Autowired
ProducerTemplate template;
#Before
public void setUp() throws Exception {
TEST_QUEUE = routes.getTEST_QUEUE();
MOBILE_QUEUE = routes.getMOBILE_QUEUE();
context.getRouteDefinition("test2phone").adviceWith(context, new Routes() {
#Override
public void configure() throws Exception {
interceptSendToEndpoint(TEST_QUEUE)
.skipSendToOriginalEndpoint()
.to("mock:testQ");
interceptSendToEndpoint(MOBILE_QUEUE)
.skipSendToOriginalEndpoint()
.to("mock:result");
}
});
context.start();
}
#Test
public void testTest() throws Exception {
String body = "hello123";
MockEndpoint resultEndpoint = context.getEndpoint("mock:result", MockEndpoint.class);
resultEndpoint.expectedMessageCount(1);
resultEndpoint.expectedBodiesReceived(body);
template.sendBody(TEST_QUEUE, body);
resultEndpoint.assertIsSatisfied();
}
#After
public void TearDown() throws Exception {
context.stop();
}
}
interceptSendToEndpoint is useful to intercepting output endpoint. You probably want replace input endpoint and intercept output endpoint. See AdviceWith.
This should work:
context.getRouteDefinition("test2phone").adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
replaceFromWith("direct:test");
interceptSendToEndpoint(MOBILE_QUEUE)
.skipSendToOriginalEndpoint()
.to("mock:result");
}
});
And test your route with:
template.sendBody("direct:test", body);

Spring bean mocking and notice that called a method which annotated by #ExceptionHandler

I try to throw a specific exception when a method call via using doThrow. Then i expect to handle it by a method in its superclass which is annotated #ExceptionHandler.
1. Should i use Spy or Mock object for my controller class(it is a spring bean)
2. Should i use InjectMocks
3. Should i test within spring context because of ExceptionHandler class which is belonged to Spring
Here is my rough view of Controller class:
DefaultPageController extends SuperClass
{
#RequestMapping(method = RequestMethod.GET)
public String get(final Model model, final HttpServletRequest request, final HttpServletResponse response)
throws CMSItemNotFoundException
{....}
}
And ParentClass of my Controller(it is abstract)
public abstract class SuperClass
{...
#ExceptionHandler(InvalidCsrfTokenException.class)
public String handleInvalidCsrfTokenException(final InvalidCsrfTokenException exception, final HttpServletRequest request)
{
request.setAttribute("message", exception.getMessage());
LOG.error(exception.getMessage(), exception);
return FORWARD_PREFIX + "/404";
}
...
}
Finally my test class:
#IntegrationTest
//#RunWith(SpringJUnit4ClassRunner.class)
public class PageRedirectOnCSRFTest
{
#Mock
private Model model;
#Mock
private HttpServletRequest request;
#Mock
private HttpServletResponse response;
#Mock
private InvalidCsrfTokenException invalidCsrfTokenException;
#Mock
private MissingCsrfTokenException missingCsrfTokenException;
#InjectMocks
#Resource
private DefaultPageController controller;
#Before
public void setUp()
{
MockitoAnnotations.initMocks(this);
try
{
doThrow(invalidCsrfTokenException).when(controller).get(model, request, response);
}
catch (final Exception e)
{
}
}
//private final DefaultPageController controller = Mockito.spy(new DefaultPageController());
// private final InvalidCsrfTokenException invalidCsrfTokenException = new InvalidCsrfTokenException(
// Mockito.mock(CsrfToken.class), "1234");
// private final MissingCsrfTokenException missingCsrfTokenException = new MissingCsrfTokenException("1234");
#Test
public void testIfCalledHandleInvalidCsrfTokenException()
{
try
{
controller.get(model, request, response);
}
catch (final Exception e)
{
// YTODO Auto-generated catch block
Assert.assertTrue(e instanceof InvalidCsrfTokenException);
Mockito.verify(controller, Mockito.times(1)).handleInvalidCsrfTokenException(invalidCsrfTokenException, request);
}
}
}
Thx and brgs

Testing Spring MVC #ExceptionHandler method with Spring MVC Test

I have the following simple controller to catch any unexpected exceptions:
#ControllerAdvice
public class ExceptionController {
#ExceptionHandler(Throwable.class)
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
#ResponseBody
public ResponseEntity handleException(Throwable ex) {
return ResponseEntityFactory.internalServerErrorResponse("Unexpected error has occurred.", ex);
}
}
I'm trying to write an integration test using Spring MVC Test framework. This is what I have so far:
#RunWith(MockitoJUnitRunner.class)
public class ExceptionControllerTest {
private MockMvc mockMvc;
#Mock
private StatusController statusController;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.standaloneSetup(new ExceptionController(), statusController).build();
}
#Test
public void checkUnexpectedExceptionsAreCaughtAndStatusCode500IsReturnedInResponse() throws Exception {
when(statusController.checkHealth()).thenThrow(new RuntimeException("Unexpected Exception"));
mockMvc.perform(get("/api/status"))
.andDo(print())
.andExpect(status().isInternalServerError())
.andExpect(jsonPath("$.error").value("Unexpected Exception"));
}
}
I register the ExceptionController and a mock StatusController in the Spring MVC infrastructure.
In the test method I setup an expectation to throw an exception from the StatusController.
The exception is being thrown, but the ExceptionController isn't dealing with it.
I want to be able to test that the ExceptionController gets exceptions and returns an appropriate response.
Any thoughts on why this doesn't work and how I should do this kind of test?
Thanks.
I just had the same issue and the following works for me:
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.standaloneSetup(statusController)
.setControllerAdvice(new ExceptionController())
.build();
}
This code will add ability to use your exceptions controlled advice.
#Before
public void setup() {
this.mockMvc = standaloneSetup(commandsController)
.setHandlerExceptionResolvers(withExceptionControllerAdvice())
.setMessageConverters(new MappingJackson2HttpMessageConverter()).build();
}
private ExceptionHandlerExceptionResolver withExceptionControllerAdvice() {
final ExceptionHandlerExceptionResolver exceptionResolver = new ExceptionHandlerExceptionResolver() {
#Override
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(final HandlerMethod handlerMethod,
final Exception exception) {
Method method = new ExceptionHandlerMethodResolver(ExceptionController.class).resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(new ExceptionController(), method);
}
return super.getExceptionHandlerMethod(handlerMethod, exception);
}
};
exceptionResolver.afterPropertiesSet();
return exceptionResolver;
}
Since you are using stand alone setup test you need to provide exception handler manually.
mockMvc= MockMvcBuilders.standaloneSetup(adminCategoryController).setSingleView(view)
.setHandlerExceptionResolvers(getSimpleMappingExceptionResolver()).build();
I had same problem a few days back, you can see my problem and solution answered by myself here Spring MVC Controller Exception Test
Hoping my answer help you out
Use Spring MockMVC to emulate a servletContainer to a point where you can incorporate any request filtering or exception handling tests in your unit tests suite.
You can configure this setup with the following approach:
Given a custom RecordNotFound exception...
#ResponseStatus(value=HttpStatus.NOT_FOUND, reason="Record not found") //
public class RecordNotFoundException extends RuntimeException {
private static final long serialVersionUID = 8857378116992711720L;
public RecordNotFoundException() {
super();
}
public RecordNotFoundException(String message) {
super(message);
}
}
... and a RecordNotFoundExceptionHandler
#Slf4j
#ControllerAdvice
public class BusinessExceptionHandler {
#ExceptionHandler(value = RecordNotFoundException.class)
public ResponseEntity<String> handleRecordNotFoundException(
RecordNotFoundException e,
WebRequest request) {
//Logs
LogError logging = new LogError("RecordNotFoundException",
HttpStatus.NOT_FOUND,
request.getDescription(true));
log.info(logging.toJson());
//Http error message
HttpErrorResponse response = new HttpErrorResponse(logging.getStatus(), e.getMessage());
return new ResponseEntity<>(response.toJson(),
HeaderFactory.getErrorHeaders(),
response.getStatus());
}
...
}
Configure a tailored test context: set a #ContextConfiguration to specify the classes you need for your test. Set Mockito MockMvc as a servlet container emulator and set your tests fixture and dependencies.
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {
WebConfig.class,
HeaderFactory.class,
})
#Slf4j
public class OrganisationCtrlTest {
private MockMvc mvc;
private Organisation coorg;
#MockBean
private OrganisationSvc service;
#InjectMocks
private OrganisationCtrl controller = new OrganisationCtrl();
//Constructor
public OrganisationCtrlTest() {
}
....
Configure a mock MVC "servlet emulator": register handler beans in the context and build the mockMvc emulator (Note: there are two possible configuration: standaloneSetup or webAppContextSetup; refer to the documentation). The builder rightfully implements the Builder pattern so you can chain configuration commands for exception resolvers and handlers before calling build().
#Before
public void setUp() {
final StaticApplicationContext appContext = new StaticApplicationContext();
appContext.registerBeanDefinition("BusinessExceptionHandler",
new RootBeanDefinition(BusinessExceptionHandler.class, null, null));
//InternalExceptionHandler extends ResponseEntityExceptionHandler to //handle Spring internally throwned exception
appContext.registerBeanDefinition("InternalExceptionHandler",
new RootBeanDefinition(InternalExceptionHandler.class, null,
null));
MockitoAnnotations.initMocks(this);
mvc = MockMvcBuilders.standaloneSetup(controller)
.setHandlerExceptionResolvers(getExceptionResolver(appContext))
.build();
coorg = OrganisationFixture.getFixture("orgID", "name", "webSiteUrl");
}
....
Get the exception resolver
private ExceptionHandlerExceptionResolver getExceptionResolver(
StaticApplicationContext context) {
ExceptionHandlerExceptionResolver resolver = new ExceptionHandlerExceptionResolver();
resolver.getMessageConverters().add(
new MappingJackson2HttpMessageConverter());
resolver.setApplicationContext(context);
resolver.afterPropertiesSet();
return resolver;
}
Run your tests
#Test
public void testGetSingleOrganisationRecordAnd404() throws Exception {
System.out.println("testGetSingleOrganisationRecordAndSuccess");
String request = "/orgs/{id}";
log.info("Request URL: " + request);
when(service.getOrganisation(anyString())).
thenReturn(coorg);
this.mvc.perform(get(request)
.accept("application/json")
.andExpect(content().contentType(
.APPLICATION_JSON))
.andExpect(status().notFound())
.andDo(print());
}
....
}
Hope this helps.
Jake.
Try it;
#RunWith(value = SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(classes = { MVCConfig.class, CoreConfig.class,
PopulaterConfiguration.class })
public class ExceptionControllerTest {
private MockMvc mockMvc;
#Mock
private StatusController statusController;
#Autowired
private WebApplicationContext wac;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
#Test
public void checkUnexpectedExceptionsAreCaughtAndStatusCode500IsReturnedInResponse() throws Exception {
when(statusController.checkHealth()).thenThrow(new RuntimeException("Unexpected Exception"));
mockMvc.perform(get("/api/status"))
.andDo(print())
.andExpect(status().isInternalServerError())
.andExpect(jsonPath("$.error").value("Unexpected Exception"));
}
}
This is better:
((HandlerExceptionResolverComposite) wac.getBean("handlerExceptionResolver")).getExceptionResolvers().get(0)
And do not forget to scan for #ControllerAdvice beans in your #Configuration class:
#ComponentScan(basePackages = {"com.company.exception"})
...tested on Spring 4.0.2.RELEASE

Resources