Showing posts with label Servlets. Show all posts
Showing posts with label Servlets. Show all posts

Tuesday, September 6, 2011

How transparently can you implement open session in view late in development?

On the Recordings project, we set up hibernate to eagerly load everything. I'm not sure why we made that decision (it was 8 months ago) but we've been busy getting rid of that practice. When deciding how to handle the lazy loading problem, we found a few different solutions:

  1.  Create different presentation objects for different pages and eagerly load everything into those -- pages that only need certain properties would get a class only containing those properties. 
  2. Add flags to our data access methods that indicate what to fetch
  3. Keep the db connection open while the view is being rendered (known as the Open Session in View pattern)
Approach (1) seems like a sound approach (and we've done it before in other projects) but would add too much additional maintenance and work this late in the development, as we're trying to restrict ourself to bug fixes and performance enhancements as necessary.

Approach (2) really seems ugly because not only does the data layer get convoluted -- it exposes the idea of "lazy loading" to other layers that shouldn't care about it, since they now have to decide what items they need.

So we've taken approach #3. We've implemented it by using a servlet filter that runs before any servlets. It simply opens a new session and then closes the session after the request is completed. One drawback is that if anything goes wrong with the database, the code that's responsible isn't shown in the stack trace. Instead, it shows the servlet filter as the cause which is not helpful.

A good (though painful) thing is that it's forced us to consolidate all of our data access code into one class. We have more than one class in our data layer, but they all extend one class (called BasicHibernateDataSource) which handles acquiring the session object. Our db tests also failed after doing this until we set up a basic database test class that mimics the servlet filter.

Although it seems like a lot, I think the effort required to implement it has still been "less" effort than the other two approaches, or at least the most transparent. This has allowed us to avoid introducing many new bugs due to adding a bunch of different functionality a this stage -- we're trying to push on so we can get it live as soon as possible.

Tuesday, April 26, 2011

More servlet testing with Mockito and JUnit

Hello everyone,

I had some time to implement some more functionality for The Project today, so I thought I'd share more with you about testing our servlets.

Last time we were testing a server's JSON response to make sure it called the right validators and handled bad inputs.

Today we're testing the response of a POST request (also via AJAX). Our target: A simple controller that handles some very basic security (sufficient for our purposes). We want to send a request to the server and set a session variable if the user is authenticated. We are using JUnit integrated into Eclipse.

So first, in our TestAdminController*:

import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;

 Naturally we'll have more than one test case, so we will put some of the common code into a setup() method that is run before each test case is executed:

private AdminController controller; 
private HttpServletRequest stubRequest;
private HttpServletResponse stubResponse; 
private HttpSession stubSession;  

@Before
public void setup()
{
    controller = new AdminController();     
    stubRequest = mock(HttpServletRequest.class);
    stubResponse = mock(HttpServletResponse.class); 
    stubSession = mock(HttpSession.class);  
}


As always we've stubbed the request and response. This time we're also stubbing a session since we'll be setting a session attribute.

Now we set up one of our test cases: simply testing that, given a valid user, the session variable is set by the server. To do this we first hook up our HttpServletRequest so that when the server attempts to retrieve the current session, it returns a stub instead. We also setup a stub user manager (which handles authentication) so that we don't have to worry about usernames and passwords and all those things:

when(stubRequest.getSession()).thenReturn(stubSession);
when(stubRequest.getParameter("password")).thenReturn(UserManager.SUPER_SECRET_ADMIN_PASSWORD); 
UserManager stubUserManager  = new UserManager(); 
when(stubUserManager.userIsAuthenticated(Mockito.anyString(), Mockito.anyString())).thenReturn(true)

We use Mockito.anyString() because we don't care what credentials have been retrieved, but we always declare the user as valid. This decouples the test from the UserManager class.

Finally, we are ready to initialize the controller:
AdminController controller = new AdminController();
controller.setUserManager(stubUserManager);
controller.doPost(stubRequest, stubResponse);         

To ensure that the server set the session variable, we use verify() to determine that the HttpSession.setAttribute() method was called:

verify(stubSession).setAttribute("authenticated", true);

Now we can run the test and if everything is working, it should pass. In this way, we have ensured that at least some parts of the controller are working so any problems will be on the client.

Well, that's all for now!

*I put the imports here because I hate it when I find a solution to something, but the author doesn't list the imports and the library isn't easily searchable!

Thursday, April 21, 2011

Unit testing java servlets with Mockito and JUnit

Hey everyone,

SO last time I shared with you about experimenting with code coverage tools. I've been considering code coverage now when writing new functionality and adding tests. One thing I've noticed is that our controllers (servlets) have no test coverage. Now I know you might say "but Chris, you shouldn't be having eg. domain logic in your controllers that need testing anyway!"

That's true guys, I agree. And we really try and maintain strict separation between the domain and controllers. Consider our testing goals for our controllers as the step before functional testing (via HtmlUnit for example). We just want to make sure that when the controller sends off the response, nothing is broken before it gets to the view.

To accomplish this, we can use a mocking framework (such as Mockito) to stub out the HttpServletRequest and HttpServletResponse interfaces that the servlet get/post methods contain.

Our target: a controller used to handle AJAX comment posting requests. When the user posts a comment, the server needs to:
  1. parse the parameters and make sure they're all valid 
  2. make sure the user hasn't tried to post too many comments in a short amount of time 
  3. validate and sanitize the fields in case they contain malicious code (eg. javascript) 
  4. create a comment and persist it to the data layer
  5. send the new comment object back as a JSON object for display 
The controller doesn't actually do most of the work: it delegates it to the domain/data layers for validation and persistence. But we can still test to see if the servlet actually generates a correct response.

Let's say we want to check to see that the controller is doing its job sending a potential comment to the CommentValidator before it tries to post it.  We do this by sending a request with invalid parameters (such as a blank user name field) to the servlet.

First for our imports:
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
Then we create our stub HttpServletRequest object:
HttpServletRequest stubRequest = mock(HttpServletRequest.class);
Now we add the expected method calls/return values to the stub request. One parameter that the server should use is "type" specifying what kind of comment is being posted. So we can indicate that when the server retrieves the "type" parameter, it can return one of our choosing:
when(stubRequest.getParameter("type")).thenReturn("recording");
We do this for the rest of our comment's fields. Now we need a way to determine if the server validated the comment or not. In our case, the controller will send a JSON representation of a CommentValidationResult (which contains a message and an error type). For a blank text field, the error code will be BLANK_FIELD.

So let's test for the presence of BLANK_FIELD in our response (which will be a JSON string). To do this we create a StringWriter and a PrintWriter and then inject them into a stub response:
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);        
HttpServletResponse stubResponse = mock(HttpServletResponse.class);       
when(stubResponse.getWriter()).thenReturn(pw); 
Here we create a stub HttpServletResponse and inject our new PrintWriter so that when the servlet writes to the response's output stream, it will write to our StringWriter instead.

Now we call the method on the controller and make sure it contains the BLANK_FIELD:
controller.doGet(stubRequest, stubResponse);
String result = sw.getBuffer().toString();
assertTrue(result.contains("BLANK_FIELD")); 
We can run the test now and it will tell us if the controller sent back an error due to our comment having blank fields (as it should have done).

We can use this approach to test how the server responds to weird URL strings that are sent by mean people to disrupt our website in addition to testing for normal functionality. Maybe next time we'll try and test the view layer...


*Oh, and I finally integrated syntax highlighting courtesy of SyntaxHighlighter