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.