Two minute tutorial

This guide assumes you are familiar with the basics of EJBs and unit-testing with JUnit. Skim it to get the basic idea - the really interesting bits of code are in bold to make it even easier to skim.

The classes we want to test

We are going to call a method on a stateless session bean, called GreetingService which will say "hello". The GreetingService is made up of a home and remote interface and an implementing class, that look like this:

public interface GreetingServiceHome extends EJBHome {
    GreetingService create() throws RemoteException, CreateException;
}

public interface GreetingService extends EJBObject {
    String greet() throws RemoteException;
}

public class GreetingServiceBean implements SessionBean {
    public String greet() {
        return "hello";
    }

    // session bean lifecycle methods omitted...
}

Note: Package names have been removed for brevity.

The unit test

We want our test to do the following:

  1. Get a JNDI context.
  2. Look up the home interface.
  3. Cast it to a GreetingServiceHome using the standard RMI object narrowing mechanism.
  4. Get a remote stub.
  5. Call a method on the remote stub.
  6. Return it to the pool.
  7. Make sure it all worked.

Wrapped up in a JUnit test case, the code looks like this:

public class GreetingServiceTest extends TestCase {

    public void testShouldSayHello() throws Exception {
        // execute
        Context context = new InitialContext();                 // 1
        Object obj = context.lookup("ejb/greetingService");     // 2
        GreetingServiceHome home =
            (GreetingServiceHome)PortableRemoteObject.narrow(   // 3
                obj, GreetingServiceHome.class);
        GreetingService service = home.create();                // 4
        String result = service.greet();                        // 5
        service.remove();                                       // 6

        // verify
        assertEquals("hello", result);                          // 7
    }
}

If we just run this test as is, it will fail. Probably with an error with the default naming context (com.sun.enterprise.something blaming org.omg.CORBA.something for not finding a bunch of config files).

Getting it working - the easy way

Assuming you have an ejb-jar.xml, you can get XJB to do all the legwork by simply passing it a java.io.Reader:

public void setUp() throws Exception {
    new XjbEjbJarParser().read(new FileReader("/path/to/ejb-jar.xml"));
}

And that's it! The test passes and we are in business.

The ejb-jar.xml processing is pretty comprehensive, so you get all your <ejb-link>, <env-ref> and <resource-ref> setup for free. What's more, you can read multiple ejb-jar.xml files, and circular references will be resolved properly.

Getting it working - the programmatic way

If you don't have an ejb-jar.xml or you want a more fine-grained setup, you can configure XJB programmatically. So what is the simplest way we can get this test to pass? We need to associate the home and remote interfaces with a bean instance, give it a name and put it in the JNDI tree. To do all this, we use an EjbConfigurator like so:

public void setUp() throws Exception {
    EJBHome home = new XjbEjbConfigurator().registerSessionBean(
            "greetingService",
            GreetingServiceHome.class,
            GreetingService.class,
            new GreetingServiceBean()
            EjbConfigurator.STATELESS);
}

This single method call creates a fully-functional implementation of GreetingServiceHome, with all the associated plumbing and metadata. It also installs XJB as the default JNDI implementation, and registers the EJB under several common names, including "ejb/greetingService", so there is nothing more to do, and we can test our EJB outside of the container.

Summary

To recap:

Where next?

If you want to stub out JDBC or do end-to-end integration testing with XJB (guess what? it's easy) you should take a look at configuring JDBC DataSources, and maybe read about how you can use Null Objects to make testing easier.

Document History