Unit Testing Servlets

You should be familiar with JUnit as it is what OpenMRS uses for unit testing. For my test, I used JUnit4. Please see The unit testing tutorial and The module unit testing tutorial for more information. It is assumed for the sake of this tutorial that you have read and understand those.

This is a brief tutorial on how to unit test a servlet using httpunit. There are two examples that exist in the OpenMRS subversion repository. I will discuss how to test HTTP requests.

First, you must add the following jar files to your module's classpath. These are included in htmlunit's distributed zip file in the jar directory.

Classpath

In lib-common add:

  • xercesImpl-2.6.1.jar

Then in lib add the following:

  • js-1.6R5.jar
  • nekohtml-0.9.5.jar
  • httpunit.jar

Add these to your eclipse classpath. For the method I used, you must deploy your module, and it must be running in OpenMRS.

Writing your test

A fair warning, I've written my test in Groovy, however the concepts are all there, and should be obvious.

Now that you have your classpath set up, it is assumed that you will allow eclipse to manage your imports for you, so I will only show you one method for the sake of brevity. Then I will explain it line by line.

WebConversation wc

    @After
    void cleanUp() {
        wc = null
    }

The above line is needed so that we can clean up after we finish testing. That should be the first method in your test class. Now let's write our first test method, I'm testing my AJAX Servlet which is responsible for validation/generation of the controller/view for the /wiki/spaces/AR/pages/18513993.

I am only going to post one test method here, for the full class see this link. It should give you the overall gist. Assume for the sake of this test method that a static import exists for assertTrue and assertEquals (as that is the case in my test class.)

@Test
    void testCheckSyntaxForModel() {
        wc = new WebConversation()
        def rq = new PostMethodWebRequest("http://localhost:8080/openmrs/moduleServlet/groovyforms/createGroovyForm")
        rq.setParameter "clazz", "class MyForm { }"
        rq.setParameter "validate", "true"
        rq.setParameter "name", "My Form"
        rq.setParameter "version", "1.0"
        def response = wc.getResponse(rq)
        assertEquals "<result>true</result>", response.text

        // test when no class definition is given
        rq.setParameter "clazz", ""
        response = wc.getResource(rq)
        assertEquals "<result>false</result>", response.text

        // test invalid syntax
        rq.setParameter "clazz", "class MyForm { String }"
        wc = new WebConversation()
        response = wc.getResponse(rq)
        assertTrue "Should be true", response.text.indexOf("Exception") > 0
    }

What's going on here? Let's see.

  1. wc = new WebConversation()
    1. This is how we mock the browser session
  2. def rq = new PostMethodWebRequest("http://localhost:8080/openmrs/moduleServlet/groovyforms/createGroovyForm")
    1. Next, we "fake" a form POST operation.
  3. The next few lines, i just define the request parameters.
  4. def response = wc.getResponse(rq)
    1. Now we actually "submit" the request.
  5. Finally, I check that the syntax is correct by retrieving the the text from the response (using response.text). It is valid.

Then after that, I check to see if it returns the correct response if no code is given; that works. Finally, I check to see that giving invalid code returns an exception, and indeed it does.

Final Notes Regarding Groovy

  1. When you see "response.text" it is NOT direct field access, groovy calls the getter for that field, in this case getText().
  2. When you see "def" it is using duck-typing, the type is inferred based on the value to the right.
  3. Parenthesis are optional, except in the case of no-arg methods.
  4. Semi-colons are optional, except when you have multiple statements on the same lines.

Other Links

Questions??

Hop onto IRC; my nick is r0bby, and I'll be happy to answer any questions, my contact info is also available on my Robby O'Connor, feel free to contact me with questions.

FAQ

  • What is @After
    • The @After annotation tells JUnit to run this method after each test is complete.
  • What is @Test
    • The @Test annotation tells JUnit to that this method is a test, and should be run.