Unit testing Spring MVC app with tests based on actual View DOM - java

We have a Spring MVC webapp, and we're currently able to unit test the controllers with MockHttpServletRequest objects.
Our tests look similar to:
request = new MockHttpServletRequest(_mockServletContext, "PUT", "/createNewUser.action");
request.setParameter("username", "bob123");
response = new MockHttpServletResponse();
userController.createNewUser(request, response);
// Ensure the user was created, etc.
Then, we query the underlying database (and other controllers) to ensure that things worked properly.
However, this does not test the actual JSP pages that we're shipping. Just the fair-weather mocked request.
What we would like to accomplish, is being able to mock the entire process from the perspective of an actual user viewing the page in a browser, like:
request = new MockHttpServletRequest(_mockServletContext, "GET", "/newUser.html");
response = new MockHttpServletResponse();
userController.newUser(request, response);
//Pseudo-code!
DOM thePage = SomeBuilder.fromResponse(response);
thePage.getTextField("usernameField").setText("bob123");
thePage.getButton("submitUserButton").click();
// Then, we test the same assertions
...
This helps us establish unit tests based on the view as the client will actually see it, and would involve ensuring that the proper HTML components exist, and the Javascript backing the button click is also sending the proper parameters to the server/controller, in the event it someday requires a user's first name, for example. Or, in the case there is a typo and the HTML form actually sends the parameter "userUsername" instead of "username". We'd like to remove the manual testing requirement in this case, as there are different code paths/form layouts depending on the role of the currently-authenticated user.
Are there Java/Spring MVC based unit testing frameworks to accomplish this task? Or other code patterns that would provide similar reliability?

Related

How to send response from servlet

I have a Ajax call sent from browser to a Servlet. At Servlet some value is calculated say A. My query is how can I validate this A. I am using TestNG from where the browser starts but after that control shifts to Servlet. How should I return the value from Servlet such that in TestNG I can fetch it and validate.
solutions I can think of:
(1)
Bypass the servlet: put your business calculations in a separate method/class, and test directly. This might be enough if the focus is on the business code (while the servlet layer does trivial tasks such as extracting simple request parameters). E.g:
// logic - assuming this is the test focus, with interesting cases such as insufficient funds, limited account etc.
public class MyBank{
public void transferFunds(int fromAccountId, int toAccountId, int dollars)...
}
// servlet that happens to have trivial code that isn't so important to test
public class MyServlet{
...
int fromAccountId=Integer.parseInt(req.getParameter("fromAccountId"));
int toAccountId=Integer.parseInt(req.getParameter("toAccountId"));
int dollars= Integer.parseInt(req.getParameter("dollars"));
bank.transferFunds(fromAccountId, toAccountId, dollars)
}
(2) Use an embedded server, like Jetty.
http://www.eclipse.org/jetty/documentation/current/embedding-jetty.html
From your unit test you just launch a jetty (using 'new Server()'...) and tell it to execute the servlet.
(3) you can also invoke your servlet, injecting it with mock request/response/session etc. Spring, for example, has such mock objects.
So it's something like:
HttpServletRequest mockReq=new MockHttpServletRequest();
HttpServletRequest mockResp=new MockHttpServletResponse();
new MyServlet().service(mockReq, mockResp);
Just note it might require some tweaking depending on what your servlet needs - e.g. request parameters, session (the mock request has methods to add them)

How to understand server/client data transfer in Angular, when coming from JSP?

I have a good basic knowledge of how to create web applications using java and jsp, together with Expression Language and JSTL. I am trying to learn how to use Angular.js for my front end.
I've gone through several tutorials, and I am starting to get a fair grip of the basics. But I have yet to figure out how to transfer data from the server, to the front end. Most tutorials I've found, describe how to send data from the front end, to the server.
I know that a RESTful api back end is recommended for Angular web apps. Unfortunately, I have no experience with this, and I find it hard to learn both angular and RESTful at the same time. If possible, I would love to make a work around, so that I can use my existing server solution, and learn one element at the time.
Server side setup
Now, in my old setup, using javax.servlet.http.HttpServlet, i call the service(HttpServletRequest, HttpServletResponse)-method. Inside the method, i add attributes to the request, like this:
request.setAttribute("attribute1", "1");
request.setAttribute("attribute2", "2");
request.setAttribute("attribute3", "3");
Then, because of a front controller pattern, I pass these request variables on to a redirect like this:
request.getRequestDispatcher("/WEB-INF/" + myPageVar + ".jsp").
forward(request, response);
For now, I would ideally like to keep this server side setup.
Current client side data access:
In the current setup, I can now access the initiated variables in two different ways. Either in a javascript script, like below (does not work with objects, only simpler attributes like strings (including JSON)). the next lines of code is picked from a jsp-page that the servlet would have redirected to.
var attribute1 = ${requestScope.attribute1};
or in the html, like this (would work with objects):
<c:set var="attribute1" value="${requestScope.attribute1}"></c:set>
I guess that I could incorporate Expression Language, and use javascript variables to initialize variables in my angular modules, directives and controllers, but I would prefer to do it purely in Angular.
Are these attributes accessible in any way, using angular? I've been trying to read up on $http, and $scope, but there is a jungle of non-relevant info on those, which I haven't been able to navigate through yet.
If the data you want to make accessible to angular should be ready when user lands on the page, it could make sense to put data in javascript variable in jsp page as you suggest.
Since your var is in global scope you can get in your angular controller like this:
$scope.att1 = attribute1;
However if you want to update your data without re-rendering the whole page (and that is what you want pretty soon) you should use $http to call a servlet that returns json. You can relatively simply make this servlet without jax-rs by overriding doGet in httpServlet and use a lightweight json lib (like gson). This example will do it:
//Set up pojo and make it into json string:
SomeClass pojo =new SomeClass();
pojo.setX("this is X");
JSONObject jsonObject = new Gson().toJson(pojo);
String jsonStr=jsonObject.toJSONString();
//Modify response and write json string
httpServletResponse.setStatus(200);//We are ok
httpServletResponse.setContentType("application/json");
httpServletResponse.setCharacterEncoding("UTF-8");
Writer writer = httpServletResponse.getWriter();
writer.write(jsonStr);
writer.close();
//Thats it
In a simple setup you handle this response in your controller like this:
$http({method: 'GET', url: 'http://yourservleturl' })
.success(function(jsonStringFromServlet){
$scope.newData = jsonStringFromServlet;
})
.error(function(){
$scope.error = true;
});
In page-html you access the data with
<div>This is your new x: {{newData.x}}</div>
Don't use jsps at all, don't set attributes.
Only use static html, thats the benefit ! You can write angular directives that perform the funcationality of jsp includes, and have much cleaner code (no embedded jstl, java. just pure html).
Create server side code that returns json. Then your angular js code calls the rest api and populates the client side model.
If you have something that needs populating on startup, use javascript appropriately.
Here is a typical java method that returns json, using Jersey (similar to Spring MVC, resteasy, restlets, spark, apache cxf etc etc) :
#PATH("/myPojo")
#GET
public Response getPojo(Long id) {
Pojo pojo = myService.getPojo(id)
return Response.ok()
.entity(pojo)
.build();
}
In angualr you can then create, for example, $myPojoService.getPojo() that is injected to relevant controllers and calls this endpoint. When called it probably returns the pojo as json and then probably populates the $scope.model.pojo json object. Then the two way databinding of angular updates your gui ... and Boom, you are a full stack engineer/ front end dev!
There are a lot of different ways to accomplish this. This is just what I ended up using.
To expose values in your JSP to Angular you'll need to write them out in script tags and build up Javascript vars with them. Then you can access them from Angular. I'm doing this to pass-in authenticated user account information from server side to my angular code. Your JSP would contain code such as:
<script>
window.CURRENT_USER = {
id: <%=currentUser.getId()%>,
name: "<%=currentUser.getName()%>",
email: "<%=currentUser.getEmail()%>",
prevLogin: new Date(<%=currentUser.getPrevLoginAt().getTime()%>),
prevLoginIp: "<%=currentUser.getPrevLoginIp()%>"
};
</script>
Then in your angular controllers, you can access it like this:
var currentUser = $window.CURRENT_USER;
A better approach (mentioned by Jacob Nicolaisen) would be to use the Google GSon library to actually generate the JSON objects instead of hand coding them.)

Passing variables to a POST request from an outside controller

Since you guys have been very helpful in my early steps into the Play Framework (thanks for that), here it goes again:
We have a working registration controller, that POSTS all credentials to the database.
But then, we want to make it possible to be immeadiately logged in afterwards. Below is the code that makes this work:
public static void doRegistration(#Valid User user) {
//registering the user
try{
SecureController.authenticate(user.username, user.password, false, "MainController.index");
}catch(Throwable ex){
MainController.index();
}
This works fine, but it is not very safe because it GETs all the credentials to the server. I know I have to edit my routes file somehow, but I can't see how.
The routes file:
* /account SecureController.login
POST /account/register RegistrationController.doRegistration
GET /account/register SecureController.login
Somewhere should be the action SecureController.authenticate, but what do I have to put in the column after the POST... It can't be /account/register, because that fails...
Thank you beforehand!
I am not sure I understand your issue. The routes file is just a way to configure your URLs to be pretty URLs. If you don't specify them, then it falls back on default {controller}/{method} syntax.
The issue you are having, is that when you call another controller Play performs a redirect to that controller's method, which involves sending a request back to your browser telling it to redirect (this ensures that the state of the application is reflected in the URL within the browser). A redirect needs therefore to send a GET request, and included in the GET request will be your parameters.
what you are trying to do, as you said, is not safe. What you should do (not the only option, only one possibility) is:
Maintain your current doRegistration action for the user
Create a service class (that does not inherit Controller). It can be static or require instantiation (with static methods should be enough though).
Add a #Before method to a common controller that will be executed always. One way is to create a controller with a #Before method and add this controller to all other controllers via the #With annotation, so that #Before will be executed always for all controllers. It requires you to add a #With to each new controller, but I believe it keeps the code quite clean.
The idea would be that the controller calls the authenticate method from the service class. It's a simple static This method checks the user (if it's enabled, has proper license, whatever) and sets some parameters in the session (via Session object).
To help with this you may want to create another authenticate method in the user that returns the attributes to set (for example in a Map, if it contains an "error" key the user can't be authenticated for some reason). How to do this step can change according to your requirements.
Once the Session has been set, you redirect to the page of your election (main, profile, etc). As you have the common #Before method, this will be executed. This method should verify the credentials in the session (user authenticated, license type, etc) and act accordingly. You have an example in the Secure controller of Play, but you could create your own.
With this, you could user the authenticate method of the service from any controller, allowing authentication via multiple methods, and using a common point to verify the session.

How can I unit test a servlet with a request that just consists of xml content

I'm trying to unit test a java WFS web service implementation. The service can accept requests containing KVP params such as:
http://www.someserver.com/wfs&SERVICE=WFS&VERSION=1.1.0&REQUEST=GetFeature&TYPENAME=InWaterA_1M
or it can also accept a request containing an XML fragment such as
<?xml version="1.0" ?>
<GetFeature version="1.1.0" service="WFS" maxFeatures="10000"
xmlns="http://www.opengis.net/wfs"
xmlns:myns="http://www.someserver.com/myns"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.opengis.net/wfs ../wfs/1.1.0/WFS.xsd">
<Query typeName="myns:InWaterA_1M"/>
</GetFeature>
I'm testing the KVP way using ServletUnit, which is straight forward:
ServletUnitClient sc = servletRunner.newClient();
WebRequest request = new PostMethodWebRequest( "http://www.someserver.com/wfs
request.setParameter( "SERVICE", "WFS );
...
request.setParameter( "TYPENAME" "InWaterA_1M" );
sc.getResponse( request);
I can't figure out how to create a corresponding request for the XML type of request though. Any ideas? I'd rather not have to use another testing framework library unless absolutely necessary.
You can create a do the following:
Create a XML of the request you want..
Create a MockHttpServletRequest
API: http://static.springsource.org/spring/docs/2.0.x/api/org/springframework/mock/web/MockHttpServletRequest.html
Call setContent(xml); and setContentType("text/xml");
Call your servlet method directly. e.g. someServlet(mockReq,mockRes);
This way there is no need to fire up the servlet container while jUnit testing...
From a quick look at the docs, it seems that ServletUnitClient can support POST requests, as well as GET requests with KVP style arguments, like you are using now: http://httpunit.sourceforge.net/doc/tutorial/task1editor-form.html
Request with XML works like posting an HTML form, only that you don't necessarily have the HTML UI in front of it.
However, I would probably break out the unit testing of the XML parsing to a separate test, and not test it explicitly through the servlet. The servlet is acting as an HTTP frontend for the XML parsing and other parts of the WFS service, and you should unit test those parts separately (perhaps you're already doing that, disregard this part in that case).
In my experience, testing the HTTP/frontend of a service is usually the least important part, the logic behind it is much more likely to break, and therefore more important to test. Also, testing the service logic separately from the frontend often forces you to use a better design.
Of course, if you have to the time, or the frontend itself involves a lot of logic, you should unit test that part as well.

Help designing velocity template to support Header and Footer

My websites pages are broken down into 3 parts:
Header
Main Content
Footer
The header needs to access the user object to display things like the username, sign/signout links depending if the user is logged in or not.
I want my velocity templates to be like this:
inject header template, that will need access to the user object.
main content will be a spring mvc action page
footer
I know that in my spring mvc action, I will simple load my velocity template with the attributes/objects that I will need to display in the 'main content' area.
Is there a way for me to provide the 'header template' access to the attributes/objects it needs from a place OTHER than the spring mvc action?
I am new to java/spring, is there a request level collection that I can load my user object into, so I can reference it from there?
I think that the way to do this in the Spring MVC framework is to use a custom Handler Interceptor that attaches the necessary information to the request (or response) object and then returns true to allow the request to proceed to the designated controller.
(Warning: I've never done this, YMMV, etc ...)

Categories