Showing posts with label Mockito. Show all posts
Showing posts with label Mockito. Show all posts

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