The Source for Java Technology Collaboration
User: Password:
Register | Login help    

Search

Online Books:
java.net on MarkMail:


A Simple Servlet for Running JUnit in Glassfish

Posted by cayhorstmann on February 14, 2009 at 8:37 PM PST

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

unit-test-in-browser.png

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

Comments
Comments are listed in date ascending order (oldest first)

Apache Cactus has been there for years http://jakarta.apache.org/cactus/

Thanks, but Cactus is much more heavyweight than what I was looking for. Maybe because it has been there for many years :-)

You could also have a look at easygloss.dev.java.net It could certainly handle injecting your injectables for you ;-) It also might be easier, with EasyMock, for running the tests outside of the servlet container

Thanks for the tip about easygloss. Of course, what I really want is not to have to worry at all but find a way for the container to do the injection. Is there some way to locate the ambient app server, hand it an object, and say "hey, inject this"?

Hi, That's actually a nice way of doing it. I've worked on projects where we had a whole maven build that used a small container (OpenEJB) to load our EJB 3.0 components and test them. Setting it up was a pain as you can imagine. Now I'm looking for a way to do something simpler as part of a pet project of mine and this fits the bill nicely. You could even run this during deploy and throw a fat Runtime exception when a test fails and thus bail out of the deploy. Thanks

Thanks--I agree that using OpenEJB is a reasonable approach. I understand there is now (or there soon will be) a way of embedding Glassfish in a similar way, but I haven't yet figured out the setup.