This is the class which i am trying to test,
public class AuthErrorResponseWriter {
#Autowired
TransResponse svcResponse;
#Override
public void writeResponse(HttpServletResponse response) {
//Set the Http status
response.setStatus(HttpStatus.FORBIDDEN.value());
svcResponse.setMessage(Constants.AUTHENTICATION_ERROR);
svcResponse.setStatus(HttpStatus.FORBIDDEN.toString());
ObjectMapper mapper = new ObjectMapper();
//Write the response
try {
Writer writer = response.getWriter();
writer.write(mapper.writeValueAsString(svcResponse));
writer.flush();
writer.close();
} catch (IOException ioex) {
logger.error("Problem producing authentication error http response",ioex);
}
}
}
The unit test code i have written is below,
RunWith(SpringRunner.class)
#WebMvcTest({AuthErrorResponseWriter .class})
#ComponentScan("com.demo.service")
public class AuthErrorResponseWriterTest {
#Mock
HttpServletResponse responseMock;
#Before
public void setUp(){
MockitoAnnotations.initMocks(this);
}
#Test
public void testResponse(){
TransResponse mockResponse = new TransResponse();
mockResponse.setMessage(Constants.AUTHENTICATION_ERROR);
mockResponse.setStatus(HttpStatus.FORBIDDEN.toString());
AuthErrorResponseWriter authErrorWriter = new AuthErrorResponseWriter ();
PrintWriter writerMock = mock(PrintWriter.class);
try {
when(responseMock.getWriter()).thenReturn(writerMock);
} catch (IOException ioex) {
//assertTrue(false);
}
authErrorWriter.writeResponse(responseMock);
verify(responseMock).setStatus(HttpStatus.FORBIDDEN.value());
}
}
When i execute this Junit, am getting a null pointer exception for
svcResponse.setMessage(Constants.AUTHENTICATION_ERROR);
svcResponse is null, even though i have mocked it.
Please can someone point to me why its not picking up the mock object and looking for the actual.
Also if my writing the Junit is a proper way?
You may want to use Mockito's runner instead of Spring (from what I see, you do not need Spring's context at all):
#RunWith(MockitoJUnitRunner.class)
public class SubscriptionServiceTest {
#InjectMocks
private AuthErrorResponseWriter authErrorResponseWriter;
#Mock
TransResponse svcResponse;
#Mock
HttpServletResponse responseMock;
....
authErrorWriter.writeResponse(responseMock);
svcResponse is null, even though i have mocked it.
No, you haven't mocked it. This is what you are doing in your code:
TransResponse mockResponse = new TransResponse();
mockResponse.setMessage(Constants.AUTHENTICATION_ERROR);
mockResponse.setStatus(HttpStatus.FORBIDDEN.toString());
What you should be doing is something like this:
#Mock
private TransResponse mockResponse;
You'll have to inject the mocks in the Target class like this:
#InjectMocks
private AuthErrorResponseWriter authErrorWriter;
And use, authErrorWriter instead of creating a new instance of the class in your test.
And then you can do something like this:
Mockito.doNothing().when(mockResponse).setMessage(Constants.AUTHENTICATION_ERROR);
Related
I have two microservices Microservice A ( context path - /abc ) and microservice B (context path - /def )
Example URLs: test.domain.com/abc/endpoint1 ,test.domain.com/def/endpoint2
In one of the apis of Microservice A ( test.domain.com/abc/endpoint1) internally its making call to Microservice B (/def/endpoint2) -> the prefix for this internal call is generated as follows
(Extract the domain from the request and then append /def/endpoint2 to make a rest call the total url will become as (test.domain.com/def/endpoint2)
Problem : When we are writting unit test cases starting controller level we are using TestRestTemplate
For this testing we need to use http://localhost:portnumber/abc/endpoint1 to test ..
Now the url of the def service also will be derived as http://localhost:portnumber/def/endpoint2
How to mock this response ( Note: We cannot use mock server on same port, we will get port binding exception) . Is there any workaround for the same?
Is there any way to have gateway kind of setup while using TestRestTemplate to route http://localhost:portnumber/def/* calls to get response from mockserver and http://localhost:portnumber/abc/* to make the actual API Service under test?
You could use a ClientHttpRequestInterceptor for this and manipulate the actual URI to call if it matches the path of your second microservice.
This might be a naive protoypish implementation:
public class UrlRewriter implements ClientHttpRequestInterceptor {
#Override
public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
try {
if (httpRequest.getURI().toString().contains("/def/abc")) {
HttpRequest modifiedRequest = new MockClientHttpRequest(HttpMethod.GET, new URI("http://localhost:8888/def/abc"));
return clientHttpRequestExecution.execute(modifiedRequest, bytes);
} else {
return clientHttpRequestExecution.execute(httpRequest, bytes);
}
} catch (URISyntaxException e) {
e.printStackTrace();
return null;
}
}
}
And then you can provide a custom bean of type RestTemplateBuilder for your test that is picked up by the TestRestTemplate:
#SpringBootTest(webEnvironment = RANDOM_PORT)
public class TestOne {
#TestConfiguration
static class TestConfig {
#Bean
public RestTemplateBuilder restTemplateBuilder() {
return new RestTemplateBuilder().interceptors(new UrlRewriter());
}
}
#Autowired
private TestRestTemplate testRestTemplate;
#Test
public void test() {
assertNotNull(testRestTemplate);
testRestTemplate.getForObject("/abc/endpoint1", String.class);
}
}
I am trying to write some unit tests for a controller in Spring MVC, and part of the controller method has the follwing code:
try {
newProjectFile.setFileType(fileType);
newProjectFile.setContent(BlobProxy.generateProxy(file.getInputStream(), file.getSize()));
} catch (Exception e) {
throw new BadUpdateException(e.getMessage());
}
I've set up a MockMultipartFile in my unit test, and would like to test the exception case here so that I can get a bad request response.
I've tried setting up something like the following:
unit test:
MockMultipartFile file = new MockMultipartFile("file", "receipts.zip", "application/zip", "".getBytes());
[...]
when(file.getInputStream()).thenThrow(IOException.class);
[...]
and I get the following error:
when() requires an argument which has to be 'a method call on a mock'.
For example:
when(mock.getArticles()).thenReturn(articles);
If I can't use 'when' on a MockMultipartFile like I would any normal mock object, and Mockito doesn't allow you to mock static methods, how can I get an exception to be thrown here?
Edit:
as mentioned in the commments, the MockMultipartFile is not from Mockito, hence the error mentioned above.
The question really is how to throw an exception in the try/catch block, which is presumably either by throwing an IOException on file.getInputStream(), or an UnsupportedOperationException on BlobProxy.generateProxy(), so that my method throws the BadUpdateException.
So my colleague found a good way to get around this using an anonymous inner class:
#Override
public InputStream getInputStream() throws IOException {
throw new IOException();
}
};
This means that an exception is thrown in the try/catch block in the controller method when trying to get the InputStream from the MockMultipartFile, and the result is the BadUpdateException.
Here is the complete code for uploading an Excel file as multipart file. This is based on M Hall's previous response, so he/she should take credit for it.
This is the controller which will allow you to make the upload:
public class MyController {
#PostMapping({"/upload"})
public String upload(#RequestParam("excelFile") MultipartFile excelFile) {
try {
//This should throw an IOException
InputStream in = excelFile.getInputStream();
} catch (IOException e) {
//handle exception
}
return "redirect:/index";
}
}
This is how the test should look like:
#SpringBootTest(classes = {MyController.class})
public class MyControllerTest {
public static final String CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
public static final byte[] CONTENT = "xml content".getBytes();
//Create a custom mock multipart file. This file will throw an IOException, when the method getInputStream is called.
CustomMockMultipartFile excelFile = new CustomMockMultipartFile("excelFile", "MyExcelFile.xlsx", CONTENT_TYPE, CONTENT);
#Autowired
private WebApplicationContext wac;
#Autowired
MyController myController;
#Test
public void testUploadIoException() throws Exception {
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.multipart("/upload").file(excelFile))
.andExpect(redirectedUrl("/index"))
.andReturn();
//Perform other assertions based on your business needs and test specifications
//Assert that the logic in the catch block is executed
}
//A private inner class, which extends the MockMultipartFile
private class CustomMockMultipartFile extends MockMultipartFile {
public CustomMockMultipartFile(String name, String originalFilename, String contentType, byte[] content) {
super(name, originalFilename, contentType, content);
}
//Method is overrided, so that it throws an IOException, when it's called
#Override
public InputStream getInputStream() throws IOException {
throw new IOException();
}
}
}
I have a method which send post request in third party resource and returns CloseableHttpResponse result. I try (in first time) to implement junit test for my project.. I know how to test methods which returns simple objects but I have no idea - how to possible similar test method?
public CloseableHttpResponse POST(String path, Map<String, String> parameters) throws URISyntaxException, IOException {
List<NameValuePair> pairParameters = generateListOfNameValuePair(parameters);
URI uri = new URIBuilder()
.setScheme(SSL_SCHEME)
.setHost(HOST)
.setPath(path)
.build();
HttpRequestBase postMethod = new HttpPost(uri);
try {
((HttpPost) postMethod).setEntity(new UrlEncodedFormEntity(pairParameters, "UTF-8"));
} catch (UnsupportedEncodingException initE) {
initE.printStackTrace();
}
return session.getHttpClient().execute(postMethod, session.getHttpContext());
}
If you use spring boot, you can use Mockito unit test.
this is an example to show you how to implement mockMvc and unit test method.
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
public class StockPlanControllerTest {
#Autowired
public WebApplicationContext context;
public MockMvc mockMvc;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
}
#Test
public void POST(String path, Map<String, String> parameters) throws URISyntaxException, IOException {
mockMvc.perform(post(path)
.contentType(MediaType.APPLICATION_JSON)
.param("paramkey", "paramvalue"))
.andExpect(status().isOk());
}
To learn more about Mockito unit test, this tutorial help you more.
mockito tutorial
I am creating my controller and controller advice like this:
Test class:
#RunWith(SpringRunner.class)
#SpringBootTest
public class TestController {
private MockMvc mockMvc;
#Mock
private MyService myService;
#Autowired
#InjectMocks
private MyController myController;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
//Build the controller mock handler
mockMvc = MockMvcBuilders
.standaloneSetup(MyController.class)
.setControllerAdvice(new MyControllerAdvice())
//This also doesn't work
//.setHandlerExceptionResolvers(createExceptionResolver())
.build();
}
//This also did not work
private ExceptionHandlerExceptionResolver createExceptionResolver() {
ExceptionHandlerExceptionResolver exceptionResolver = new ExceptionHandlerExceptionResolver() {
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) {
Method method = new ExceptionHandlerMethodResolver(MyControllerAdvice.class).resolveMethod(exception);
return new ServletInvocableHandlerMethod(new MyControllerAdvice(), method);
}
};
exceptionResolver.afterPropertiesSet();
return exceptionResolver;
}
/**
* Tests passing bad input to see if our exception handler is called.
*/
#Test
public void testBadRequest()
{
//Make a request object that has a bad input (e.g. bad date string)
MyRequest request = new MyRequest();
//Set the request values
request.setDate( "a" );
try
{
myController.getSomething( request );
}
catch (Exception e)
{
//It reaches here without ever reaching my controller advice in debugging
e.printStackTrace();
}
}
}
Controller advice:
#EnableWebMvc
#ControllerAdvice
#Component
public class MyControllerAdvice {
#ExceptionHandler(value = Exception.class)
public ResponseEntity<String> handleException(HttpServletRequest request, Exception exception) throws Exception
{
//This is never called (I'm using a debugger and have a breakpoint here)
return new ResponseEntity<String>(
"test",
HttpStatus.INTERNAL_SERVER_ERROR
);
}
}
There are two issues in your example:
MockMvcBuilders#standaloneSetup() receives Controller objects as parameters, not the Class objects. So it should be:
mockMvc = MockMvcBuilders
.standaloneSetup(new MyController())
.setControllerAdvice(new MyControllerAdvice())
.build();
You are calling myController.getSomething( request ) directly, while you should use previously built mockMvc. Direct call is unadvised as it's not processed with TestDispatcherServlet. Here is a couple of examples for mockMvc requests:
GET
mockMvc.perform(get("/testSomething"))
.andExpect(status().is5xxServerError())
.andReturn();
POST
mockMvc.perform(post("/testSomething")
.contentType(MediaType.APPLICATION_JSON)
.content(json)) //it's JSON string
.andExpect(status().is5xxServerError())
.andReturn();
I want to perform a test on a controller method which throws an exception. The method is something like this:
#RequestMapping("/do")
public ResponseEntity doIt(#RequestBody Request request) throws Exception {
throw new NullPointerException();
}
When I try to test this method with following code part,
mockMvc.perform(post("/do")
.contentType(MediaType.APPLICATION_JSON)
.content(JSON.toJson(request)))
NestedServletException is thrown from Spring libraries. How can I test that NullPointerException is thrown instead of NestedServletException?
Our solution is rather a workaround: The exception is caught in advice and error body is returned as HTTP response. Here is how the mock works:
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller)
.setHandlerExceptionResolvers(withExceptionControllerAdvice())
.build();
private ExceptionHandlerExceptionResolver withExceptionControllerAdvice() {
final ExceptionHandlerExceptionResolver exceptionResolver = new ExceptionHandlerExceptionResolver() {
#Override
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(final HandlerMethod handlerMethod, final Exception exception) {
Method method = new ExceptionHandlerMethodResolver(TestAdvice.class).resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(new TestAdvice(), method);
}
return super.getExceptionHandlerMethod(handlerMethod, exception);
}
};
exceptionResolver.afterPropertiesSet();
return exceptionResolver;
}
Advice class:
#ControllerAdvice
public class TestAdvice {
#ExceptionHandler(Exception.class)
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Object exceptionHandler(Exception e) {
return new HttpEntity<>(e.getMessage());
}
}
After than, following test method passes successfully:
#Test
public void testException
mockMvc.perform(post("/exception/path"))
.andExpect(status().is5xxServerError())
.andExpect(content().string("Exception body"));
}
Easier way is to inject #ExceptionHandler into your Spring Test Context or it throws exception right in MockMvc.perform() just before .andExpect().
#ContextConfiguration(classes = { My_ExceptionHandler_AreHere.class })
#AutoConfigureMockMvc
public class Test {
#Autowired
private MockMvc mvc;
#Test
public void test() {
RequestBuilder requestBuilder = MockMvcRequestBuilders.post("/update")
.param("branchId", "13000")
.param("triggerId", "1");
MvcResult mvcResult = mvc.perform(requestBuilder)
.andExpect(MockMvcResultMatchers.status().is4xxClientError())
.andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(__ -> Assert.assertThat(
__.getResolvedException(),
CoreMatchers.instanceOf(SecurityException.class)))
.andReturn();
}
That way MvcResult.getResolvedException() holds #Controller's exception!
https://stackoverflow.com/a/61016827/173149