Spring Test Service Autowiring resulting in null - spring

I am able to run a Rest Controller PUT method that uses a Autowired #Service as expected via the Spring Boot Application. The same Autowiring is failing while trying to perform a Spring JUnit Test. I have tried reading through multiple threads with similar issues. I made sure I am NOT creating the #Service through the "new" keyword and I tried Context Configuration and other methods .. but all seems to be in vain. I am not sure where I am going wrong.
My Spring Boot Application class -
#SpringBootApplication
#ComponentScan({
"com.initech.myapp.*"
})
public class IngestionServerApplication {
private static final Log logger = LogFactory.getLog(IngestionServerApplication.class);
public static void main(String[] args) {
SpringApplication.run(IngestionServerApplication.class, args);
logger.info("Ingestion Server Application started...");
}
}
Rest Controller Class -
package com.initech.myapp.ingestion.controller;
#RestController
public class IngestionController extends BaseRestController {
private static final Log logger = LogFactory.getLog(IngestionController.class);
// This variable is getting "null" autowiring if invoked
// via Spring Unit Testing framework while it is injected fine via
// Spring Boot app invocation.
#Autowired
public IngestionPayloadProcessor payloadProcessor;
#RequestMapping(path = "/ingest", method = RequestMethod.PUT,
consumes = {
MediaType.APPLICATION_JSON_VALUE,
MediaType.APPLICATION_XML_VALUE
},
produces = {
MediaType.APPLICATION_JSON_VALUE
})
public IngestionSuccessResponse ingest(#RequestHeader(value = "authToken", required = true) String authToken,
#RequestBody String jsonBody) throws Exception {
IngestionPayload ingestionPayload = new IngestionPayload();
ingestionPayload.setAuthToken(authToken);
ingestionPayload.setJsonBody(jsonBody);
IngestionSuccessResponse ingestionSuccessResponse = payloadProcessor.process(ingestionPayload);
return ingestionSuccessResponse;
}
}
Service Class
package com.initech.myapp.ingestion.app.service;
#Service
#ImportResource({"spring.xml"})
public class IngestionPayloadProcessor {
private static final Log logger = LogFactory.getLog(IngestionPayloadProcessor.class);
#Resource(name = "kafkaConfig")
private Properties kafkaConfig;
#Value("${kakfaTopic}")
private String kakfaTopic;
public IngestionSuccessResponse process(IngestionPayload ingestionPayload) throws Exception {
try {
IngestionSuccessResponse ingestionSuccessResponse = buildSuccessResponse(ingestionPayload);
return ingestionSuccessResponse;
}
catch (IllegalStateException e)
{
logger.error("Encountered exception while dropping message in Kafka... " + e.getMessage());
throw e;
}
}
}
private buildSuccessResponse() { ... }
Spring Unit Testing
#RunWith(SpringRunner.class)
#ContextConfiguration(locations = "classpath*:/spring.xml")
#WebMvcTest(IngestionServerApplication.class)
public class IngestionServerApplicationTests {
#Autowired
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.standaloneSetup(
new IngestionServiceController())
.build();
}
#Test
public void testIngestService() throws Exception {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("authToken","safdafio12312asdfs23");
RequestBuilder requestBuilder = put("/ingest").content("{'testKey' : 'testVal'}").accept(MediaType.APPLICATION_JSON).headers(httpHeaders);
this.mockMvc.perform(requestBuilder).andExpect(status().isOk());
}
}
Error Logs
2016-08-10 19:24:36.500 DEBUG 7505 --- [ main] m.m.a.RequestResponseBodyMethodProcessor : Read [class java.lang.String] as "application/json" with [org.springframework.http.converter.StringHttpMessageConverter#49aa766b]
2016-08-10 19:24:36.510 DEBUG 7505 --- [ main] .m.m.a.ExceptionHandlerExceptionResolver : Resolving exception from handler [public com.initech.myapp.ingestion.model.IngestionSuccessResponse com.initech.myapp.ingestion.app.controller.myappIngestionServiceController.ingest(java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String) throws java.lang.Exception]: java.lang.NullPointerException
2016-08-10 19:24:36.512 DEBUG 7505 --- [ main] .m.m.a.ExceptionHandlerExceptionResolver : Invoking #ExceptionHandler method: public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> com.initech.myapp.base.controller.BaseRestController.handleException(java.lang.Exception,javax.servlet.http.HttpServletRequest)
This is the error handler...
2016-08-10 19:24:36.514 INFO 7505 --- [ main] p.d.i.a.c.myappIngestionServiceController : > handleNoResultException
2016-08-10 19:24:36.574 DEBUG 7505 --- [ main] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Written [{status=500, authToken=6acb1a5c-2ced-4690-95b3-eb7957c7c28a, error=null}] as "application/json" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter#50d3bf39]
java.lang.AssertionError: Status
Expected :200
Actual :500
Note that I have debugged through the test and I can see that the NullPointer exception is thrown at the below line in the Rest Controller Class as the payloadProcessor object is null.
IngestionSuccessResponse ingestionSuccessResponse = payloadProcessor.process(ingestionPayload);
=====
Unit Test with Mock objects: (This is working as expected)
#RunWith(SpringRunner.class)
#ActiveProfiles("dev")
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#AutoConfigureMockMvc
public class IngestionServerUnitTests {
#Autowired
private MockMvc mockMvc;
#MockBean
private IngestionPayloadProcessor processor;
// I was able to get this to work by removing the setUp() method
// that was originally in my code. It was trying to build a new instance
// of the REST controller and then run the "perform" on top of it
// which was causing the test to fail I assume!
/*#Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.standaloneSetup(
new IngestionServiceController())
.build();
}*/
#Test
public void testIngestService() throws Exception {
IngestionSuccessResponse ingestionSuccessResponse = new IngestionSuccessResponse();
ingestionSuccessResponse.setStatusCode("200");
ingestionSuccessResponse.setRequestId("6acb1a5c-2ced-4690-95b3-eb7957c7c28a");
ingestionSuccessResponse.setReceivedTimestamp("2016-08-09T19:43:30.02234312");
given(this.processor.process(anyObject())).willReturn(ingestionSuccessResponse);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("Authorization","5e18685c95b34690");
RequestBuilder requestBuilder = put("/ingest").content("<test>test data</test>").accept(MediaType.APPLICATION_JSON).headers(httpHeaders);
this.mockMvc.perform(requestBuilder).andExpect(status().isOk());
}
}

When you specify #WebMvcTest there are only certain components of your application that are added to the ApplicationContext. The annotation is actually a composition of a bunch of other annotations as described in the docs: https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/autoconfigure/web/servlet/WebMvcTest.html
Based on this your IngestionPayloadProcessor doesn't get instantiated as a bean, and shouldn't as you are telling the test to only run tests for the web layer. What you need to do is specify a #MockBeanfor the IngestionPayloadProcessor within the test and then define a mock for the method that the controller is calling.
#RunWith(SpringRunner.class)
#WebMvcTest(IngestionServerApplication.class)
public class IngestionServerApplicationTests {
#Autowired
private MockMvc mockMvc;
#MockBean
private IngestionPayloadProcessor processor;
#Test
public void testIngestService() throws Exception {
given(this.processor.process(anyObject())).willReturn(new InjestionSuccessResponse());
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("authToken","safdafio12312asdfs23");
RequestBuilder requestBuilder = put("/ingest").content("{'testKey' : 'testVal'}").accept(MediaType.APPLICATION_JSON).headers(httpHeaders);
this.mockMvc.perform(requestBuilder).andExpect(status().isOk());
}
}
Details on the new features of Spring Boot 1.4 testing are here: https://spring.io/blog/2016/04/15/testing-improvements-in-spring-boot-1-4
* Update based on comments *
Realized you could just auto configure the MockMvc and not need to use the TestRestTemplate. I haven't tested this but it should work.
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class IngestionServerApplicationTests {
#Autowired
private MockMvc mockMvc;
#Test
public void testIngestService() throws Exception {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("authToken","safdafio12312asdfs23");
RequestBuilder requestBuilder = put("/ingest").content("{'testKey' : 'testVal'}").accept(MediaType.APPLICATION_JSON).headers(httpHeaders);
this.mockMvc.perform(requestBuilder).andExpect(status().isOk());
}
}

Related

how to test rest controller with custom spring DispatcherServlet

I'm trying to test my controller and I want to my custom DispatcherServlet include on that.
my dispatcher is something like this:
public class LoggableDispatcherServlet extends DispatcherServlet {
private static final Logger LOGGER = LoggerFactory.getLogger(LoggableDispatcherServlet.class);
#Autowired
private LogsService logsService;
#Override
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws
Exception
{
.
.
.
mapper.readTree(requestWrapper.getContentAsByteArray());
int status = responseWrapper.getStatus();
JsonNode newNode = mapper.readTree(responseWrapper.getContentAsByteArray());
JsonNode responseJson= newNode;
logsService.addLogs(request,response ,...)
}
}
and my test Class is something like this:
#SpringBootTest
#RunWith(SpringRunner.class)
public class RestControllerTest {
private final Resource test1 = new ClassPathResource("test1.json");
private MockMvc mockMvc;
#Autowired
private RestTemplate restTemplate;
#Autowired
private WebApplicationContext wac;
#BeforeEach
public void before() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
this.mockRestServiceServer = MockRestServiceServer.bindTo(this.restTemplate)
.build()
}
#Test
public void getAvailableLoanTest() throws Exception{
String url1="http://localhost:80801/testService?param1=1&param2=2";
this.mockRestServiceServer
.expect(ExpectedCount.manyTimes(), requestTo(url1))
.andExpect(method(HttpMethod.GET))
.andRespond(withSuccess(this.test1, MediaType.APPLICATION_JSON_UTF8));
this.mockMvc.perform(post("/api/myApi")
.contentType(MediaType.APPLICATION_JSON)
.content(asJsonString(new MyRequest(1, 2,))))
.andExpect(status().is2xxSuccessful())
.andExpect(content().contentType("application/json"))
//.andExpect(jsonPath("#.res1").value(1L))
.andReturn();
.
.
.
}
}
The problem is my request receive directly by rest controller and I can't test the DispatcherServlet.
I'm using spring boot 2.2.3 with jar packaging and I don't have web.xml or any servlet configration.

Spring Boot testing with Junit

Using Spring Boot 2.0.3.RELEASE
The goal of the test is have a service call a controller in the same app.
Here is the simplified setup I am trying
The app class
#SpringBootApplication
public class StartApp {
public static void main(String[] args) {
SpringApplication.run(StartApp.class, args);
}
}
The controller class
#RestController
public class EmpCtrl {
private static final Logger logger = LoggerFactory.getLogger(EmpCtrl.class);
#Autowired
private EmpDao empDao;
#RequestMapping(value = "/emp01", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
public List<Emp> findAllEmp01() {
logger.trace("running my.demo.controller.findAllEmp01");
List<Emp> emps = new ArrayList<>();
Iterable<Emp> results = this.empDao.findAll();
results.forEach(emp -> {emps.add(emp);});
return emps;
}
}
The service class
#Service
public class GetEmpSrv {
private static final Logger logger = LoggerFactory.getLogger(GetEmpSrv.class);
public void getEmps01(){
final String uri = "http://localhost:8080/emp01";
RestTemplate restTemplate = new RestTemplate();
String result = restTemplate.getForObject(uri, String.class);
logger.debug(result);
}
}
and the Junit class
#RunWith(SpringRunner.class)
#SpringBootTest(classes = StartApp.class)
public class GetEmpSrvTest01 {
#Test
public void doCall() {
GetEmpSrv getEmpSrv = new GetEmpSrv();
getEmpSrv.getEmps01();
}
}
This is being run inside Eclipse Oxygen.3a (4.7.3a)
in the console it appears Spring Boot is running .. ic the load of h2 db and /emp01 is being mapped however in the Failure Trace of Junit ic
I/O error on GET request for "http://localhost:8080/emp01": Connection refused: connect; nested exception is java.net.ConnectException: Connection refused: connect
This makes me think the embedded Tomcat is not running. When I start Spring normally /emp01 returns a JSON as expected.
My question is: Is this type of testing possible with Junit? If so what do I need to do to make it work?
In your test, please autowire TestRestTemplate. The reason is that your spring test will run on another port and the call to http://localhost:8080 will fail.
#RunWith(SpringRunner.class)
#SpringBootTest(classes = StartApp.class)
public class GetEmpSrvTest01 {
#Autowired
TestRestTemplate testRestTemplate;
#Test
public void doCall() {
// without http://localhost:8080, testRestTemplate it will handle it for you
testRestTemplate.getForObject("/emp01", String.class);
}
}
Also you can expect a list of objects in your test:
List<Emp> emps = testRestTemplate.getForObject("/emp01", List.class);

exception handler method in #ControllerAdvice are not get called

I'm test my controller using junit5. In test method, EntityNotFoundException is throwed but exception handler is not called.
I have tried declaring ExceptionHandlerExceptionResolver bean with order 1. But it didn't works.
Exception handler which handles EntityNotFoundException:
#ControllerAdvice
#EnableWebMvc
public class AppWideExceptionHandler {
#ExceptionHandler(EntityNotFoundException.class)
public #ResponseBody
String handleEntityNotFoundException(EntityNotFoundException e) {
return "test";
}
...
}
The AppWideExceptionHandler is in youshu.exception package which will be scanned because of #ComponentScan({"youshu.controller", "youshu.service","youshu.exception"}) annotated on WebConfig class.
The processRefundApplication controller method calls RefundService.get(String orderID) which may throw EntityNotFoundException:
#Controller
#RequestMapping("/Order")
public class OrderController {
#AsSeller
#RequestMapping(value = "/{orderID}/RefundApplication",
method = RequestMethod.PATCH,
params = "isApproved",
produces = "application/json")
#Transactional(rollbackFor = RuntimeException.class)
public #ResponseBody
Map processRefundApplication(#SessionAttribute("user") User user,
#PathVariable("orderID") String orderID,
#RequestParam("isApproved") boolean isApproved) {
...
}
debug information:
...
17:58:34.575 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource - Returned connection 5312115 to pool.
17:58:34.576 [main] DEBUG org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver - Resolving exception from handler [public java.util.Map youshu.controller.OrderController.processRefundApplication(youshu.entity.User,java.lang.String,boolean)]: youshu.exception.EntityNotFoundException: 找不到订单ID为20180419182220001的退款申请
17:58:34.576 [main] DEBUG org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver - Resolving exception from handler [public java.util.Map youshu.controller.OrderController.processRefundApplication(youshu.entity.User,java.lang.String,boolean)]: youshu.exception.EntityNotFoundException: 找不到订单ID为20180419182220001的退款申请
17:58:34.576 [main] DEBUG org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver - Resolving exception from handler [public java.util.Map youshu.controller.OrderController.processRefundApplication(youshu.entity.User,java.lang.String,boolean)]: youshu.exception.EntityNotFoundException: 找不到订单ID为20180419182220001的退款申请
17:58:34.577 [main] DEBUG org.springframework.test.web.servlet.TestDispatcherServlet - Could not complete request
youshu.exception.EntityNotFoundException: 找不到订单ID为20180419182220001的退款申请
at youshu.service.RefundService.get(RefundService.java:23) ~[classes/:?]
...
test class:
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {WebConfig.class, RootConfig.class, DataConfig.class})
#WebAppConfiguration
class OrderControllerTest {
#Autowired
OrderController controller;
#Autowired
UserService userService;
#Autowired
OrderService orderService;
private User customer;
private User seller;
private HashMap<String, Object> sessionAttrs;
private ResultMatcher success = jsonPath("$.code")
.value("0");
private MockMvc mockMvc;
#BeforeEach
void init() {
if (customer == null) {
customer = new User();
customer.setID(18222);
customer.setName("shijiliyq");
customer.setPassword("...");
customer.setPaymentPassword("...");
}
if (seller == null) {
seller = new User();
seller.setID(27895);
}
if (sessionAttrs == null) {
sessionAttrs = new HashMap<>();
sessionAttrs.put("user", customer);
}
if (mockMvc == null)
mockMvc = standaloneSetup(controller).build();
}
#Test
void processRefundApplication() throws Exception{
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd");
String path = String.format("/Order/%s%d0001/RefundApplication"
, simpleDateFormat.format(new Date()), customer.getID());
HashMap<String,Object> sessionAttributes=new HashMap<>();
sessionAttributes.put("user",seller);
mockMvc.perform(patch(path)
.characterEncoding("UTF-8")
.param("isApproved","true")
.sessionAttrs(sessionAttributes))
.andDo(print())
.andExpect(success);
}
...
}
You need to point your mockMvc instance to your controller advice class:
#Autowired
AppWideExceptionHandler exceptionHandler;
...
mockMvc = standaloneSetup(controller).setControllerAdvice(exceptionHandler).build();

Spring Boot Test MockMvc perform post - Not working

I'm trying to make a Integration test using the Spring Boot but the post request is not working. The method saveClientePessoaFisica is never called and do not return any kind of error! I just tried to make other tests using a get method and it works properly.
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
#ActiveProfiles("dev")
public class ClienteControllerIT {
#Autowired
private MockMvc mvc;
#Test
public void nao_deve_permitir_salvar_cliente_pf_com_nome_cpf_duplicado() throws Exception {
this.mvc.perform(post("/api/cliente/pessoafisica/post")
.contentType(MediaType.APPLICATION_JSON)
.content("teste")
.andExpect(status().is2xxSuccessful());
}
}
#RestController
#RequestMapping(path = "/api/cliente")
public class ClienteController {
#Autowired
private PessoaFisicaService pessoaFisicaService;
#PostMapping(path = "/pessoafisica/post", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Void> saveClientePessoaFisica(#RequestBody PessoaFisica pessoaFisica) throws Exception {
this.pessoaFisicaService.save(pessoaFisica);
return new ResponseEntity<Void>(HttpStatus.CREATED);
}
}
Your content "teste" is no valid JSON. When I am using your code, I'm getting a JsonParseException complaining about that (By the way, there is a parenthese missing after content("teste") ). Also helpful is using andDo(print()) which will give you the request and response in more detail:
#Test
public void nao_deve_permitir_salvar_cliente_pf_com_nome_cpf_duplicado() throws Exception {
this.mvc.perform(post("/api/cliente/pessoafisica/post")
.contentType(MediaType.APPLICATION_JSON)
.content("teste"))
.andDo(print())
.andExpect(status().is2xxSuccessful());
}
Some things to look for :
Enable logging in your mockmvc
Enable your mockmvc properly
When using spring security, initialise it in mockmvc
When using spring security / CSRF / HTTP POST , pass a csrf in your mockmvc
Enable debug logging
Like this :
logging:
level:
org.springframework.web: DEBUG
org.springframework.security: DEBUG
Working test :
#RunWith(SpringRunner.class)
#SpringBootTest
#ActiveProfiles("test")
#AutoConfigureMockMvc
public class MockMvcTest {
#Autowired
protected ObjectMapper objectMapper;
#Autowired
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Before
public void init() throws Exception {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).apply(springSecurity()).build();
}
#Test
public void adminCanCreateOrganization() throws Exception {
this.mockMvc.perform(post("/organizations")
.with(user("admin1").roles("ADMIN"))
.with(csrf())
.contentType(APPLICATION_JSON)
.content(organizationPayload("org1"))
.accept(APPLICATION_JSON))
.andDo(print())
.andExpect(status().isCreated());
}
}

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