A Simple Servlet for Running JUnit in Glassfish

When teaching unit testing in the context of a simple EJB3.1 application, I was looking for an easy way of testing managed beans and session beans inside Glassfish. Of course, one can test out-of-container or use an embedded container (but I didn't quite figure out how to do that with Glassfish v3—I'd appreciate hints), or a mock container (but that seemed to require a bit of setup).

I hit upon a scheme that I had not seen elsewhere: put the unit tests in the container and trigger them with a servlet that reports the outcomes. Advantages:

  1. There is very little setup to learn
  2. It is easy to run the tests from a browser
  3. The tests run in the exact same environment as your app
  4. No need to wait for container startup with every test. (This could also be a disadvantage because the database isn't in a pristine state at the beginning of each test.)

Here is how to do it.

Add the JUnit 4 JAR to WEB-INF/lib.

Add the following servlet to your WAR file:

package myapp.servlets;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.junit.internal.JUnitSystem;
import org.junit.runner.JUnitCore;

public class TestServlet extends HttpServlet {   
   protected void doGet(HttpServletRequest request, HttpServletResponse response)
   throws ServletException, IOException {
       String className = request.getParameter("class");
       response.setContentType("text/plain");
       OutputStream out = response.getOutputStream();
       final PrintStream pout = new PrintStream(out);
       new JUnitCore().runMain(new JUnitSystem() {
         public PrintStream out() { return pout; }
         public void exit(int arg0) {}
       }, className);
       out.close();
   }
       
   protected void doPost(HttpServletRequest request, HttpServletResponse response)
   throws ServletException, IOException {
       doGet(request, response);
   }
}

Add the following entries to web.xml:

    <servlet>
        <servlet-name>TestServlet</servlet-name>
        <servlet-class>myapp.servlets.TestServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>TestServlet</servlet-name>
        <url-pattern>/test</url-pattern>
    </servlet-mapping>

Write your JUnit test case in the usual way. For example, here is a test for a session bean:

package myapp.session;

import static org.junit.Assert.assertEquals;

import javax.naming.InitialContext;
import javax.naming.NamingException;

import org.junit.Before;
import org.junit.Test;

import myapp.entity.Person;

public class UserSBTest {
   private UserSB userSB;

   @Before public void init() throws NamingException {
      InitialContext context = new InitialContext();
      userSB = (UserSB) context.lookup("java:global/MyApp/UserSB");
   }
   
   @Test public void testAddUser() {
      Person p = new Person();
      p.setFirstName("Fred");
      p.setUsername("fred");
      userSB.save(p);
      Person q = userSB.find("fred");
      assertEquals(p.getFirstName(), q.getFirstName());
      userSB.removePerson("fred");
   }   
}

Unfortunately, you can't just have the container inject the session bean.

   @EJB private UserSB userSB; // Doesn't work when JUnit loads the class

When JUnit loads the class, it doesn't deal with EJB annotations.

Then point your browser to http://localhost:8080/MyApp/test?class=myapp.session.UserSBTest

.

Proverbial exercise to the reader: Add a green bar when the test cases pass.