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

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

Related

Spring boot mapping exception cannot test

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());
}
}

How to mock methods which has parameters with #Value annotated class/member variables?

I have 3 classes involved in my problem: MyService, MyServiceTest, MyConfig and a yaml file application.yml. MyRequest and MyResponse are my custom classes which denotes the incoming request to service class from controller and outgoing response from service class to controller respectively.
I am getting below error:
SAAJ007: Bad Endpoint type
application.yml
backside:
url: https://mybackend.com/my/operation
ssl:
store: D:\\mydata\\myconfiguration\\mykey.jks
token: password
MyService.java
#Service
public class MyService{
#Value("${backside.url}")
private String backsideUrl;
private MyFormatter formatter; // has methods which formats request and response
private SOAPConnectionFactory soapFactory;
#Autowired
public MyService(MyFormatter formatter, SOAPConnectionFactory soapFactory){
this.formatter = formatter;
this.soapConnectionFactory = soapConnectionFactory;
}
public MyResponse createSomething(MyRequest request){
MyResponse response;
try{
SOAPConnection connection = soapConnectionFactory.createConnection();
SOAPMessage soapResponse = connection.call(formatter.formatRequest(request), backsideUrl); // formatRequest(MyRequest request) returns object of SOAPMessage
response = formatter.formatResponse(soapResponse); // formatResponse(SOAPMessage soapMsg) returns object of MyResponse
connection.close();
}
catch(SOAPException ex){
logger.error("SOAP Connection Error:{}", ex.toString());
throw MyCustomException("MyErrorMsg", HttpStatus.INTERNAL_SERVER_ERROR);
}
catch(Exception ex){
logger.error("Unknown Error:{}", ex.toString());
throw MyCustomException("UnknownErrorMsg", HttpStatus.INTERNAL_SERVER_ERROR);
}
return response;
}
}
MyConfiguration.java
#Configuration
public class MyConfiguration{
#Value("${ssl.store}")
private String keyPath;
#Value("${ssl.token}")
private String keyPassword;
#Bean
public SOAPConnectionFactory soapConnectionFactory() throws SOAPException{
return SOAPConnectionFactory.newInstance();
}
#Bean
public void addSSLCertificate() throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithException, UnrecoverableKeyException, KeyManagementException{
// lines of code which uses #Value annotated keyPath and keyPassword fields
}
}
MyServiceTest.java
#ExtendWith(SpringExtension.class)
public class MyServiceTest{
#Mock
private SOAPConnectionFactory soapConnectionFactory;
#Mock
private SOAPConnection soapConnection;
#Mock
private MyFormatter formatter;
#InjectMocks
private MyService service;
private MyRequest request;
private MyResponse expectedResponse;
private SOAPMessage soapRequest;
private SOAPMessage soapResponse;
#BeforeEach
void initializeObjects() throws SOAPException{
// Lines of code creating an object of MyRequest and setting its field values : request
// Lines of code creating an object of MyResponse and setting its field values : expectedResponse
// Lines of code creating an object of SOAPMessage: soapRequest
// Lines of code creating an object of SOAPMessage: soapResponse
}
#Test
public void myTestCase(){
Mockito.when(soapConnectionFactory.createConnection()).thenReturn(soapConnection);
Mockito.when(formatter.formatRequest(any(MyRequest.class))).thenReturn(soapRequest);
Mockito.when(soapConnection.call(any(SOAPMessage.class), any(String.class))).thenReturn(soapResponse);
Mockito.when(formatter.formatResponse(any(SOAPMessage.class))).thenReturn(expectedResponse);
MyResponse actualResponse = service.createSomething(request);
Assertions.assertEquals(expectedResponse.toString(), actualResponse.toString()); // overridden toString() method exists in the definition of MyResponse class
}
}
Since the value of String object i.e. backsideUrl is not mocked and hence it will not get assigned, then at the time of connection.call(formatter.formatRequest(request), backsideUrl) method execution, the value of backsideUrl will be null. So the mock is not going to working properly because we are using any(String.class).
Instead of this:
Mockito.when(soapConnection.call(any(SOAPMessage.class), any(String.class))).thenReturn(soapResponse);
Use this:
Mockito.when(soapConnection.call(any(SOAPMessage.class), any())).thenReturn(soapResponse);

How to Mock with MockHttpSession and Junit

Looked at this post where #slim gave a solution close to what I am asking about. I am trying to write unit test on the class below. I am pulling out the sessionId. (look for String sessId inside doFilterInternal method)
#Component
public class AppLoggingFilter extends OncePerRequestFilter {
private AppLoggingMDCService mdcService;
#Inject
public AppLoggingFilter(AppLoggingMDCService appLoggingMDCService) {
Assert.notNull(appLoggingMDCService, "AppLoggingMDCService must not be null");
this.mdcService = appLoggingMDCService;
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
Principal principal = request.getUserPrincipal();
String sessId = DigestUtils.sha256Hex(request.getSession().getId());
if (principal != null) {
String userId = principal.getName();
mdcService.put(AppLoggingMDCService.LOG_KEY_USER_ID, userId);
}
mdcService.put(AppLoggingMDCService.LOG_KEY_SESSION_ID, sessId);
try {
filterChain.doFilter(request, response);
} finally {
mdcService.removeAll();
}
}
Naturally the test below is failing because I don't have a valid session. Obviously I am getting a null-pointer-exception whenever i call filter.doFilterInternal(request, response, filterChain);. In the test class, the "mock-session" is not set up and has no ID. In my unit test I have this.
#RunWith(SpringJUnit4ClassRunner.class)
#ActiveProfiles(value = {"test"})
public class AppLoggingFilterUnitTest {
#Mock
AppLoggingMDCService mdcService;
#Mock
MockHttpServletRequest request;
#Mock
MockHttpServletResponse response;
#Mock
MockFilterChain filterChain;
#Mock
MockHttpSession session;
#InjectMocks
AppLoggingFilter filter;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
request.setSession(session);
}
#Test
public void testCustomerIdHasBeenLogged() throws Exception {
String customerId = "1234";
when(request.getHeader(AuthorizationConstants.CUSTOMER_KEY)).thenReturn(customerId);
filter.doFilterInternal(request, response, filterChain);
verify(mdcService, times(1)).put(AppLoggingMDCService.LOG_KEY_CUST_ID,customerId);
}
}
So coming back to my question, how do simulate as valid mocked "MockHttpSession" so my others tests don't fail?
UPDATE
So I added the session into my test class like this. In my setup method is where I said to return the "Mocked" session. The test passes only when I call
String sessId = request.getSession().getId();. if I try to do the DigestUtils.sha256Hex like
String sessId = DigestUtils.sha256Hex(request.getSession().getId()); ,all tests fail due to a null-pointer. I am not sure why. Mocking DigestUtils doesn't make much sense.
#Mock
AppLoggingMDCService mdcService;
#Mock
HttpServletRequest request;
#Mock
HttpServletResponse response;
#Mock
FilterChain filterChain;
#Mock
HttpSession session;
#InjectMocks
AppLoggingFilter filter;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(request.getSession()).thenReturn(this.session);
}
You are doing to much in your test and it is way to complex. Remove the runner, remove Mockito for the Mock* as those need to be instantiated not mocked.
public class AppLoggingFilterUnitTest {
#Mock
private AppLoggingMDCService mdcService;
#InjectMocks
private AppLoggingFilter filter;
private MockHttpServletRequest request;
private MockHttpServletResponse response = new MockHttpServletResponse();
private MockFilterChain filterChain = new MockFilterChain();
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
this.request = new MockHttpServletRequest();
this.request.addHeader(AuthorizationConstants.CUSTOMER_KEY, "1234");
}
#Test
public void testCustomerIdHasBeenLogged() throws Exception {
filter.doFilterInternal(request, response, filterChain);
verify(mdcService, times(1)).put(AppLoggingMDCService.LOG_KEY_CUST_ID,customerId);
}
}
With the creating of the mocks for the Mock* classes you basically defeated the purpose of those classes.

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

GWT - RemoteService interface and Spring - how to get HttpSession?

I am using GWT (2.5) with RPC, Spring and Postgresql for my project. My issue is about HttpSession handling .
All queries to server are dispatched by Spring (DispatchServlet) to my GwtController (extends RemoteServiceServlet) .
The particular RemoteService is injected in the GwtController . It is easy to get the HttpSession inside the GwtContorller.
In example by getThreadLocalRequest().getSession() or just from request.getSession().
My question is how to get HttpSession object inside the RemoteService ?
public class GwtRpcController extends RemoteServiceServlet {
……………
private RemoteService remoteService;
private Class remoteServiceClass;
………………
public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {
…………
}
public String processCall(String payload) throws SerializationException {
…………
}
public void setRemoteService(RemoteService remoteService) {
…………….
}
}
My Interface - DataService which implements RemoteService
public class DataServiceImpl implements DataService {
public Data getData(){
!!!!! Here I want to get HttpSession !!!!!
…………………………
}
}
You can maintain a ThreadLocal in your Servlet and store there your current Request, then expose your Request with a static method.
public class GwtRpcController extends RemoteServiceServlet {
static ThreadLocal<HttpServletRequest> perThreadRequest =
new ThreadLocal<HttpServletRequest>();
#Override
public String processCall(String payload) throws SerializationException {
try {
perThreadRequest.set(getThreadLocalRequest());
return super.processCall(payload);
} finally {
perThreadRequest.set(null);
}
}
public static HttpServletRequest getRequest() {
return perThreadRequest.get();
}
}
public class DataServiceImpl implements DataService {
public Data getData(){
HttpServletRequest request = GwtRpcController.getRequest();
HttpSession session = request.getSession();
}
}

Resources