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.
Related
Hi I have saved captcha code which is generated through a servlet in HttpSession and send it to the Angular component. Now when user send back the captcha which he sees there i am not able to compare it to the one in the HttpSession because it comes as null.
Controller
#PostMapping(value = "/register")
public GenericResponse registerNewUserAccount(#RequestBody UserVO user,
Model model, HttpServletRequest request) throws Exception {
if (user.getCaptchaResponse() != null ) {
if (user.getCaptchaResponse().equals(request.getAttribute("captcha"))) {
...
} else {
...
}
}
else {
...
} }
Seeting session as:
public class CaptchServlet extends HttpServlet {
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String captchaStr = "";
captchaStr = CaptchUtility.generateCaptchaTextMethod2(6);
try {
HttpSession session = request.getSession();
session.setAttribute("captcha", captchaStr);
OutputStream outputStream = response.getOutputStream();
ImageIO.write(cpimg, FILE_TYPE, outputStream);
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}
}
in controller session.getAttribute("captcha") is null.
add {withCredentials: true} to angular calls
this.http.get(someUrl, {withCredentials: true})
this will send cookies with the request and that is the most probable reason of your problem that cookies are not sent
I am building a mobile app and Restful API, I want the user of the app to be able to do GET what ever resources he want without Authentication. But if he want to do POST he have to enter his username and pass.
I already made a HTTP basic Authentication by putting a filter in web.xml.
<filter>
<filter-name>AuthenticationFilter</filter-name>
<filter-class>org.service.RestAuthenticationFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AuthenticationFilter</filter-name>
<url-pattern>/webapi/*</url-pattern>
</filter-mapping>
and there are the classes
public class AuthenticationService {
ClientsService s = new ClientsService();
public boolean authenticate(String authCredentials) {
if (null == authCredentials)
return false;
// header value format will be "Basic encodedstring" for Basic
// authentication. Example "Basic YWRtaW46YWRtaW4="
final String encodedUserPassword = authCredentials.replaceFirst("Basic"
+ " ", "");
String usernameAndPassword = null;
try {
byte[] decodedBytes = Base64.decode(
encodedUserPassword);
usernameAndPassword = new String(decodedBytes, "UTF-8");
} catch (IOException e) {
e.printStackTrace();
}
final StringTokenizer tokenizer = new StringTokenizer(
usernameAndPassword, ":");
final String username = tokenizer.nextToken();
final String password = tokenizer.nextToken();
boolean authenticationStatus =s.auth(username, password);
return authenticationStatus;
}
}
and the filter
public class RestAuthenticationFilter implements javax.servlet.Filter {
public static final String AUTHENTICATION_HEADER = "Authorization";
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filter) throws IOException, ServletException {
if (request instanceof HttpServletRequest) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String authCredentials = httpServletRequest
.getHeader(AUTHENTICATION_HEADER);
// better injected
AuthenticationService authenticationService = new AuthenticationService();
boolean authenticationStatus = authenticationService
.authenticate(authCredentials);
if (authenticationStatus) {
filter.doFilter(request, response);
} else {
if (response instanceof HttpServletResponse) {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse
.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
}
}
}
#Override
public void destroy() {
}
#Override
public void init(FilterConfig arg0) throws ServletException {
}
}
what i need to know is : how to pass the username and password or maybe just the id of the client to the methods of Restful after the Authentication.
A solution different to my comment: you can look for the HTTP request method and then make a decision if you call a authentication method or not:
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filter) throws IOException, ServletException {
if (request instanceof HttpServletRequest) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String authCredentials = httpServletRequest
.getHeader(AUTHENTICATION_HEADER);
boolean authenticationStatus;
// check request method
if (((HttpServletRequest).request).getMethod().equals("GET")) {
authenticationStatus=true;
} else {
// better injected
AuthenticationService authenticationService =
new AuthenticationService();
authenticationStatus = authenticationService
.authenticate(authCredentials);
}
if (authenticationStatus) {
filter.doFilter(request, response);
} else {
if (response instanceof HttpServletResponse) {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse
.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
}
}
}
Update:
With JAX-RS a possible solution for obtaining more request information may look like this:
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
#GET
#Path("offers")
#Produces("application/xml")
public YourList getOffers(#Context HttpServletRequest request)
{
System.out.println("request to "+request.getRequestURI()+" , Auth: "+request.getHeader(AUTHENTICATION_HEADER));
// more stuff for obtaining data
}
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);
I am trying to get my first servlets to work. I have found some similar problems and solutions to them, but it´s not excatly what I would like to do.
This is my login servlet:
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String username=request.getParameter("username");
String password=request.getParameter("password");
if(LoginValidator.validate(username, password)){
HttpSession session = request.getSession();
session.setAttribute("user", username);
session.setMaxInactiveInterval(30*60);
Cookie sessionCookie = new Cookie("sessionKuki", username);
sessionCookie.setMaxAge(30*60);
response.addCookie(sessionCookie);
RequestDispatcher rd=request.getRequestDispatcher("paste.jsp"); //INSTEAD of paste.jsp I would like to get session attribute called uri I set in filter. BUT I when I try to use get attribute, Eclipse says there is no attribute called URI.
rd.forward(request,response);
}
else{
out.print("Sorry username or password error");
RequestDispatcher rd=request.getRequestDispatcher("login.html");
rd.include(request,response);
}
out.close();
}
}
And there is filter that I use to redirect to login page, when user is not signed in:
public class SessionFilter implements Filter{
// private ServletContext context;
public void init(FilterConfig filterConfig) throws ServletException {
//this.context = filterConfig.getServletContext();
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
String uri = req.getRequestURI(); //THERE IS uri of the site from where the user gets redirected to login page
HttpSession session = req.getSession(false);
session.setAttribute("uri", uri); // HERE I TRY to set uri to session attribute. My intention is to use that uri in my login servlet
if(uri.endsWith(".css")) {
chain.doFilter(request, response);
return;
}
if(uri.endsWith(".js")) {
chain.doFilter(request, response);
return;
}
if(session == null && !(uri.endsWith("login.html") || uri.endsWith("login") || uri.endsWith("forgot.jsp") || uri.endsWith("signup.jsp"))){
res.sendRedirect("login.html");
System.out.print("redirecting to login");
}else{
chain.doFilter(request, response);
}
}
public void destroy() {
}
}
Is it even possible, what I am trying to do? How to do it? Is there a better way to do it? I dont want to mix html and script. My intention is that, when user comes to a pages, and trys to get access to somewhere, he is redirected to login page. And after he logs in, he should be redirected to the page he wanted to go at the beginning.
Not sure if this would work but please try doing your filter like this:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
String uri = req.getRequestURI();
HttpSession currentSession = req.getSession(false);
if(uri.endsWith(".css")) {
chain.doFilter(request, response);
return;
}
if(uri.endsWith(".js")) {
chain.doFilter(request, response);
return;
}
if(currentSession == null && !(uri.endsWith("login.html") || uri.endsWith("login") || uri.endsWith("forgot.jsp") || uri.endsWith("signup.jsp"))){
HttpSession newSession = req.getSession();
newSession.setAttribute("uri", uri);
res.sendRedirect("login.html");
System.out.print("redirecting to login");
}else{
chain.doFilter(request, response);
}
}
What i did was create a new session in the filter if the session is null.
I'm try to open form login.jsp but getting javax.servlet.ServletException
The server side component of the HTTP Monitor has detected a java.lang.StackOverflowError.
This happens when there is an infinite loop in the web module.
Correct the cause of the infinite loop before running the web module again
LoginController code :
public class LoginController extends HttpServlet {
public LoginController()
{
sandiBank = "null";
session = null;
mUserDao = new MUserDao();
sandiBIDao = new SandiBIDao();
cabangDao = new CabangDao();
parameterDao = new ParameterDao();
mAccesDao = new MAccesDao();
}
#Override
protected void doGet(HttpServletRequest httpservletrequest, HttpServletResponse httpservletresponse)
throws ServletException, IOException
{
httpservletrequest.getRequestDispatcher("./login.jsp").forward(httpservletrequest, httpservletresponse);
}
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
String user = request.getParameter("user");
String pass = request.getParameter("pass");
MUser mUser = mUserDao.getMUser(user);
if(!mUser.isCheck())
{
RequestDispatcher dispatcher = request.getRequestDispatcher("login.jsp");
dispatcher.forward(request, response);
} else
{
Cabang cabang = cabangDao.getSandiBank(mUser.getKdCab());
if(cabang.getSandiBank() != null)
sandiBank = cabang.getSandiBank();
RequestDispatcher dispatcher = request.getRequestDispatcher("home.jsp");
session = request.getSession(true);
session.setAttribute("userid", user);
session.setAttribute("passwd", pass);
String userid = (String)session.getAttribute("userid");
session.setAttribute("kdcaba", mUser.getKdCab());
session.setAttribute("sndbnk", sandiBank);
System.out.println(session.getAttribute("sndbnk"));
session.setAttribute("sandikd_bank", parameterDao.getSandiBank());
session.setMaxInactiveInterval(3600);
request.setAttribute("sandiBI", sandiBIDao.getSandiBI());
request.setAttribute("mAccesses", mAccesDao.getmAccesses(userid));
dispatcher.forward(request, response);
}
}
private static final long serialVersionUID = 1L;
private MUserDao mUserDao;
private SandiBIDao sandiBIDao;
private MAccesDao mAccesDao;
private CabangDao cabangDao;
private ParameterDao parameterDao;
private String sandiBank;
private HttpSession session;
}
This usually means that you have a recursive call in your code.
A recursion is simply a method that calls itself, causing the stack to overflow and throw the StackoverFlow exception.
check flow of your code where method call itself
Change
httpservletrequest.getRequestDispatcher("./login.jsp").forward(httpservletrequest, httpservletresponse);
To
httpservletrequest.getRequestDispatcher("/login.jsp").forward(httpservletrequest, httpservletresponse);