Skip navigation
ANNOUNCEMENT: community.oracle.com is currently Read only due to planned upgrade until 29-Sep-2020 9:30 AM Pacific Time. Any changes made during Read only mode will be lost and will need to be re-entered when the application is back read/write.

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 tohttp://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.

http://weblogs.java.net/blog/cayhorstmann/archive/tp500.jpeg A Thinkpad sale seduced me into upgrading my laptop. My feeble rationale was that I could stop dual-booting, use Linux on the new laptop and Vista on the old one. So, I got a shiny new Thinkpad T500 for under $1,000, blew away Vista Homeless Edition, and installed Ubuntu Jaunty Alpha 4. I expected the usual fussing with wireless networks, display adapters, and futile fights to activate exotic peripherals. I was totally disappointed. Everything, and I mean everything, down to the webcam, worked after a 30 minute install with one reboot and no fussing. With an alpha release.

http://weblogs.java.net/blog/cayhorstmann/archive/torvalds-laptop.jpegThis is the end of an era that began with the famous words in Linus Torvalds' original Linux announcement: “Do you pine for the days when men were men and wrote their own device drivers?” Years ago, I religiously posted pages with my install experience, such as this one, filled with arcane drivel, so that my fellow sufferers could glean a few morsels of wisdom or at least share the pain.

Now, there would be no point. Installing Linux has become so much easier than removing the crapware from pre-installed Vista that it isn't even funny. (No flames please—I am not saying that you should run Ubuntu. If you run Windows, Mac OS X, OpenSolaris, Fedora, or whatever, I am sure you have excellent reasons for your choice, and I am not trying to change your mind.)

So, what's this to do with Java?

My point is that some technologies undergo a relentless process of continuous improvement that ultimately pays off, and it is easy to be deceived by all the naysayers who were disappointed by a prior version. As we say in German: “Was lange w

Java EE 6 makes it pretty straightforward to crunch out a basic web + database application. This semester, my software engineering class is building web apps, and I decided it is simpler to have them use JSF + JPA in Glassfish v3 rather than some technology that seems easy at first and then lets them down.

Here is a laundry list of recent simplifications.

  • Author JSF pages as facelets: XHTML + JSF tags. No icky JSP syntax
  • Use annotations: @ManagedBean @Stateless @Entity @EJB @PersistenceContext. No icky XML configuration
  • Use EZNavigation™ in JSF. Actions directly return the view ID string. Hooray—no navigation infaces-config.xml
  • No more busywork interfaces for session beans
  • Use entity annotations, and let JPA generate the tables. No SQL!
  • Package everything in a single WAR

The WAR file has the structure

web pages (.xhtml)
WEB-INF/
  web.xml
  faces-config.xml
  classes/
    managed beans
    stateless session beans
    entity beans
    META-INF/
      persistence.xml

There are three cookie-cutter XML files that you never need to touch. In particular, faces-config.xml is empty! The rest is XHTML and Java.

Unfortunately, setting up your tools is a bit of trouble because everything is right now a moving target. Here are the steps.

  1. Install Glassfish v3 Prelude
  2. Add the glassfish-v3-prelude-version/glassfish/bin directory to your PATH
  3. Run glassfish-v3-prelude-version/updatetool/updatetool. Add the Glassfish EJB container. Install any available updates.
  4. Check the version of the JSF implementation in the updatetool. If it is < 2.0, delete it and go back to the list of available modules. You should get a 2.0 module that you can install. You need version 2.0.0.9 or above.
  5. Start the server: 
    asadmin start-database
    asadmin start-domain
    
  6. Download and unzip this test application. Edit build.properties and set the path of your Glassfish installation. Run ant. Point your browser to http://localhost:8080/SimpleQuiz 

    http://weblogs.java.net/blog/cayhorstmann/archive/Screenshot-SimpleQuiz.png

  7. You can use the EE version of Netbeans 6.5 out of the box. With the EE version of Eclipse 3.4, install the Eclipse Glassfish adapter . When you import the sample project into Eclipse, make a “Dynamic web project”, set the source path to src/java (not src), the web path to web (not WebContent), and uncheck “Generate deployment descriptor” . Both Netbeans and Eclipse do a beautiful job with hotswapping.

http://weblogs.java.net/blog/cayhorstmann/archive/nirvana.jpgOverall, I was very happy. This is a huge step forward from five years ago, when Bruce Tate refused to eat Elephant again.

Have we reached Nirvana? Not quite. Here are some remaining hassles:

  • I hate writing (and reading) all those getters and setters. Did I ever mention that Java needs support for properties?
  • For a simple app, it is a nuisance to separate managed beans and session beans. Web beans, erm, Java Contexts and Dependency Injection, should solve that, but I didn't have the courage to throw that into the mix. Next semester :-)
  • Error reporting has become better, in no small part thanks to facelets. But I still get the stack trace from hell more often than I'd like.