Mockito when...thenResult always returns null - java

With the above code I always get an error in line of test
when(request.getServletContext().getAttribute("SessionFactory"))
.thenReturn(factory);
Any ideas?
Java class
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
SessionFactory sessionFactory = (SessionFactory) request.getServletContext().getAttribute("SessionFactory");
...............
}
Test class
#Test
public void testServlet() throws Exception {
HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class);
factory = contextInitialized();
when(request.getServletContext().getAttribute("SessionFactory")).thenReturn(factory); //Always error here
when(request.getParameter("empId")).thenReturn("35");
PrintWriter writer = new PrintWriter("somefile.txt");
when(response.getWriter()).thenReturn(writer);
new DeleteEmployee().doGet(request, response);
verify(request, atLeast(1)).getParameter("username"); // only if you want to verify username was called...
writer.flush(); // it may not have been flushed yet...
assertTrue(FileUtils.readFileToString(new File("somefile.txt"), "UTF-8")
.contains("My Expected String"));
}

when(request.getServletContext().getAttribute("SessionFactory")).thenReturn(factory);
This bit:
request.getServletContext().getAttribute("SessionFactory")
is a chained call; you're trying to stub both the request, and the servlet context that the request returns.
You can do that, but you need to use deep stubs:
HttpServletRequest request = mock(HttpServletRequest.class, RETURNS_DEEP_STUBS);

Related

How to mock RequestDispatcher so that it goes to the .jsp file?

I am trying to write the JUnit test case for the code:
In SearchController class
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
List<AlbumSimplified> items = spotifyService.searchAlbum(searchName);
request.setAttribute("items", items);
request.getRequestDispatcher("searchResult.jsp").forward(request, response);
}
as
public void SearchControllerTesting() throws ServletException, IOException {
HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class);
//mocked myalbums
when(searchServiceMock.searchAlbum(anyString())).thenReturn(myalbums); when(request.getRequestDispatcher(anyString())).thenReturn(request.getRequestDispatcher("searchResult.jsp"));
searchController.doGet(request, response);
}
The error I am facing is:
java.lang.NullPointerException: Cannot invoke "jakarta.servlet.RequestDispatcher.forward(jakarta.servlet.ServletRequest, jakarta.servlet.ServletResponse)" because the return value of "jakarta.servlet.http.HttpServletRequest.getRequestDispatcher(String)" is null
I believe that it is due to the fact that the uri is not identified for the request and so, it is not able to find the "searchResult.jsp" located at "/app/src/main/webapp/searchResult.jsp" where app is the root of the project directory.
Hence I tried to set the
when(request.getRequestedURI()).thenReturn("app/search"), which is the URL of the request in the browser for non-testing usage.
However, I am not able to move ahead and solve the issue.
I want the items in the request to go to the searchResult.jsp, and return me a response of type "text/html".
Thanks.

ContentCachingResponseWrapper.getContentAsByteArray() is empty when testing with MockHttpServletResponse

I have a filter for logging some information for each request to a Spring Boot application. Some of this information I need to extract from the body. That's not a problem in itself, but to do so I use ContentCachingResponseWrapper, and that is messing up my unit tests.
Here is a simplified version of my filter:
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
var wrappedResponse = response instanceof ContentCachingResponseWrapper ? (ContentCachingResponseWrapper) response : new ContentCachingResponseWrapper(response);
filterChain.doFilter(request, wrappedResponse);
} finally {
System.out.println("Response body: " + new String(wrappedResponse.getContentAsByteArray()));
wrappedResponse.copyBodyToResponse();
}
}
And here is a simplified version of my test:
void myTest() throws ServletException, IOException {
final String body = "This is a body that my service might return.";
var testResp = new MockHttpServletResponse();
testResp.getWriter().print(body);
testResp.getWriter().flush();
testResp.setContentLength(body.length());
myFilter.doFilterInternal(Mockito.mock(HttpServletRequest.class), testResp, Mockito.mock(FilterChain.class));
}
The problem is that when running my tests, wrappedResponse.getContentAsByteArray() returns an empty array.
There are 2 things wrong with your code
Your filter isn't wrapping the response in the ContentCachingResponseWrapper
You are writing the response before the wrapping has occured on the underlying response, so the ContentCachingResponseWrapper has no change of caching the response.
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
var wrappedResponse = response instanceof ContentCachingResponseWrapper ? (ContentCachingResponseWrapper) response : new ContentCachingResponseWrapper(response);
filterChain.doFilter(request, wrappedResponse);
} finally {
System.out.println("Response body: " + new String(wrappedResponse.getContentAsByteArray()));
wrappedResponse.copyBodyToResponse();
}
}
Now the response will be wrapped in the wrapper for responses written further down the FilterChain. This is also something you can leverage in your testcase by mocking the FilterChain and write the response in an answer.
void myTest() throws ServletException, IOException {
var body = "This is a body that my service might return.";
var req = new MockHttpServletRequest();
var res = new MockHttpServletResponse();
var mockChain = Mockito.mock(FilterChain.class);
Mockito.when(mockChain.doFilter(any(), any())
.thenAnswer((it -> {
var response = it.getArgument(1, HttpServletResponse.class);
response.getWriter().print(body);
response.getWriter().flush();
response.setContentLength(body.length());
return null;
});
myFilter.doFilterInternal(req, res, mockChain);
}
Something along these lines should do the trick.

Test a void method that redirect foward

How can I test a void method that redirects me with RequestDispatcher?
What I made until now.
public void testAuthAction_userNull() {
HttpServletRequest requestMock = createMock(HttpServletRequest.class);
HttpServletResponse responseMock = createMock(HttpServletResponse.class);
expect(requestMock.getSession().getAttribute("user")).andReturn(null);
replay(requestMock);
AuthAction action = new AuthAction();
RequestDispatcher rd = requestMock.getRequestDispatcher("/User/login.jsp");
}
the method I want to the test is.
public void execute(HttpServletRequest request, HttpServletResponse response) {
User user = (User) request.getSession().getAttribute("User");
try {
if(user == null) {
RequestDispatcher rd = request.getRequestDispatcher("/User/login.jsp");
if(rd != null)
rd.foward(request, response);
} else {/* */}
}
catch(Exception e){/* */}
}
I'm using JUnit and EasyMock.
You need to create a mock of RequestDispatcher expecting to be forwarded, and return it from your mock:
RequestDispatcher dispatcherMock = createMock(RequestDispatcher.class);
expect(requestMock.getRequestDispatcher("/User/login.jsp"))
.andReturn(dispatcherMock);
// Expect to be forwarded.
dispatcherMock.forward(requestMock, responseMock);
EasyMock.expectLastCall().once();
replay(dispatcherMock);
replay(requestMock);
// Run your test on whatever instance has `execute`:
someInstance.execute(requestMock, responseMock);
I will provide a long answer that should be helpful I think.
So, the tested method is this.
public void execute(HttpServletRequest request, HttpServletResponse response) {
User user = (User) request.getSession().getAttribute("User");
try {
if(user == null) {
RequestDispatcher rd = request.getRequestDispatcher("/User/login.jsp");
if(rd != null)
rd.forward(request, response);
} else {/* */}
}
catch(Exception e){/* */}
}
A working test method would be this:
#Test
public void testAuthAction_userNull() {
HttpServletRequest requestMock = mock(HttpServletRequest.class);
HttpServletResponse responseMock = mock(HttpServletResponse.class);
HttpSession sessionMock = mock(HttpSession.class);
expect(requestMock.getSession()).andReturn(sessionMock);
expect(sessionMock.getAttribute("User")).andReturn(null);
expect(requestMock.getRequestDispatcher("/User/login.jsp")).andReturn(null);
replay(requestMock, sessionMock);
execute(requestMock, responseMock);
verify(requestMock, sessionMock);
}
I am using mock() instead of createMock(). It's the same but nicer and shorter.
It returns a null dispatcher because nothing more is needed. I've added a verify() to make sure everything was called as expected.
Then, if you want to make sure the forward is called as well, you also need a mock for the RequestDispatcher.
#Test
public void testAuthAction_userNull() throws Exception {
HttpServletRequest requestMock = mock(HttpServletRequest.class);
HttpServletResponse responseMock = mock(HttpServletResponse.class);
HttpSession sessionMock = mock(HttpSession.class);
RequestDispatcher rdMock = mock(RequestDispatcher.class);
expect(requestMock.getSession()).andReturn(sessionMock);
expect(sessionMock.getAttribute("User")).andReturn(null);
expect(requestMock.getRequestDispatcher("/User/login.jsp")).andReturn(rdMock);
rdMock.forward(requestMock, responseMock);
replay(requestMock, sessionMock, rdMock);
execute(requestMock, responseMock);
verify(requestMock, sessionMock, rdMock);
}
The verify() will make sure forward() is called. You do not need an expectLastCall(). It is implicit.
Then to simplify, I would actually do this:
public class MyTest extends EasyMockSupport {
#Test
public void testAuthAction_userNull() throws Exception {
HttpServletRequest requestMock = mock(HttpServletRequest.class);
HttpServletResponse responseMock = mock(HttpServletResponse.class);
HttpSession sessionMock = mock(HttpSession.class);
RequestDispatcher rdMock = mock(RequestDispatcher.class);
expect(requestMock.getSession()).andReturn(sessionMock);
expect(sessionMock.getAttribute("User")).andReturn(null);
expect(requestMock.getRequestDispatcher("/User/login.jsp")).andReturn(rdMock);
rdMock.forward(requestMock, responseMock);
replayAll();
execute(requestMock, responseMock);
verifyAll();
}
}
The EasyMockSupport class makes the code simpler.
And to be honest, in this case, when using Spring, I would use spring-test.
#Test
public void testAuthAction_userNull() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
execute(request, response);
assertThat(response.getForwardedUrl()).isEqualTo("/User/login.jsp");
}
It does the exact same thing but as you can see it is much shorter because the session and request dispatcher are created under the hood to behave like you would expect.

Get status of handling request by Spring's ResourceHttpRequestHandler.handleRequest() method

I have the code like:
#PostConstruct
public void init() throws IOException {
resourceHttpRequestHandler = new ResourceHttpRequestHandler();
resourceHttpRequestHandler.setServletContext(servletContext);
resourceHttpRequestHandler.setLocations(... my locations here... );
}
#RequestMapping(value = "/mypath/**", method = RequestMethod.GET)
public void getResource(HttpServletRequest request, HttpServletResponse response) throws Exception {
resourceHttpRequestHandler.handleRequest(request, response);
/* here */
}
Is it possible to know if the resolving of resource request was successful or not (after the call of method handleRequest() )?

how to prevent servlet from being invoked directly through browser

I was working on a web project using java servlet and jsp pages. In one of the servlet we have RequestDispatcher method and which is calling another servlet.
#WebServlet("/Demo")
public class DemoServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.sendRedirect("testing"); //calling other servlet
}
}
#WebServlet("/testing")
public class TestingServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("Hello World");
}
}
So, now I wanted to prevent contextRoot/testing from being invoked directly from the browser but instead only let it invoked from the other servlet(Demo)
Please suggest me if there is any way to do that.
Couple of techniques exist:
Look at writing a HTTP Request Filter. You can then inspect the incoming request and the url and reject it if the pattern matches the servlet paths that you do not want to be invoked directly.
Another mechanism is to use the security constraints in your web.xml to allow access to various paths in your application only to authorized users/roles. Look at <security-constraint> tag in web.xml
Answer given by "Romin" is correct. You have to use Filters for this. what you can do is, you can set a new session variable whenever "/Demo" url is accessed and in the filter check for the condition that session exists, if it exists allow the url or else throw error. You could do something similar like this. In "Demo" servlet
#WebServlet("/Demo")
public class DemoServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
HttpSession session = request.getSession() //get new session
res.sendRedirect("testing"); //calling other servlet
}
}
In Filter class add the below code
#WebFilter("/login")
public class MyFilter implements Filter{
public void init(FilterConfig arg0) throws ServletException {}
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpRequest request = (HttpRequest) req;
HttpResponse respone = (HttpResponse) res;
HttpSession session = request.getSession(false) //get the existing session object
if(null != session) {
chain.doFilter(req, resp);
} else {
"redirect to some error page or home page"
}
}
public void destroy() {}
}
One approach is to check the caller's ip using ServletRequest.getRemoteAddr() and rejects it if it's not called locally
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
if(!req.getRemoteAddr().equals("127.0.0.1")) { // reject }
}
However this method wouldn't work legitimate caller (eg: proxy) is also using the same ip.

Categories