Testing EJB3 apps with TestNG

Posted by    |      

It would be great if I could use the TestNG plugin for IntelliJ but I'm still on version 4.5 and its only available for IDEA 5. Tried to switch a few times but the XML editor just doesn't work anymore and throws exceptions faster than I can click. I want the XML editor of IDEA 3.x back, it just worked and didn't have the goofy indentation routines of 4.x...

Still, after using TestNG only for a a day or two, I think that its so much better than JUnit that I wonder why I didn't try earlier. I guess I should have listened to Gavin who was raving about how easy it was to get the Seam tests up and running. It actually has all the things I've been missing with JUnit.

So I got my first TestNG setup running nicely for CaveatEmptor. By the way, I've uploaded a new alpha release that includes all the stuff I'm going to talk about here (and quite a few other new things). For those of you interested in the progress of the /Hibernate in Action/ second edition: I've completed 95% of all mapping examples, which I think (and hope) was the most time consuming part. I'm now updating the chapters about sessions, transactions, caching, etc., so finishing the manuscript this year is possible, I guess.

Back to TestNG and how I used it to test EJBs. One of the nice things about TestNG is how easy it is to configure runtime infrastructure you need for your tests. First I wrote a class that boots the JBoss EJB 3.0 container. TestNG will run the startup() and shutdown() methods for a suite of tests:

public class EJB3Container {

    private static InitialContext initialContext;
    private EJB3StandaloneDeployer deployer;

    @Configuration(beforeSuite = true)
    public void startup() {
        try {

            // Boot the JBoss Microcontainer with EJB3 settings, loads ejb3-interceptors-aop.xml
            EJB3StandaloneBootstrap.boot(null);

            // Deploy CaveatEmptor beans (datasource, mostly)
            EJB3StandaloneBootstrap.deployXmlResource("caveatemptor-beans.xml");

            // Add all EJBs found in the archive that has this file
            deployer = new EJB3StandaloneDeployer();
            deployer.getArchivesByResource().add("META-INF/persistence.xml");

            // Deploy everything we got
            deployer.create();
            deployer.start();

            // Create InitialContext from jndi.properties
            initialContext = new InitialContext();

        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    @Configuration(afterSuite = true)
    public void shutdown() {
        try {
            deployer.stop();
            deployer.destroy();
            EJB3StandaloneBootstrap.shutdown();
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    public static Object lookup(String beanName) {
        try {
            return initialContext.lookup(beanName);
        } catch (NamingException ex) {
            throw new RuntimeException("Couldn't lookup: " + beanName, ex);
        }
    }

}

First thing on startup, I boot the JBoss Microcontainer, which is actually the kernel of the future JBoss AS 5.x. Because I'm using the EJB3StandaloneBootstrap it will automatically search for the configuration files for the EJB 3.0 container. You need those and the required libraries in your classpath. If you want to try this download CaveatEmptor and copy the libraries and configuration.

Next I'm deploying some beans from caveatemptor-beans.xml, right now this includes just a single datasource for integration testing. It is configured to use JTA and bound to JNDI, services provided by the microcontainer. You can set up and wire other stateless POJO services there if you have to.

Finally I'm deploying all EJBs for my tests. The EJB3StandaloneDeployer supports many different deployment strategies and I decided to use search the classpath and deploy the JAR that contains this file. For EJB 3.0 persistence, which is what I'm primarily testing in CaveatEmptor, I have to have a META-INF/persistence.xml configuration anyway for an EntityManagerFactory. I actually have to figure out how to deploy an exploded archive with a single command and auto-discovery of EJBs...

Now if I write a test class I will need a starting point, I need to get a handle on one of my EJBs. I can look them up in JNDI - that's what the static lookup() method is good for - or I could wire them into my test classes with the microcontainer. I decided to use a lookup:

public class CategoryItem {

    @Test(groups = {"integration.database"})
    public void saveCategory() {

        CategoryDAO catDAO = (CategoryDAO)EJB3Container.lookup(CategoryDAO.class.getName());
        Category newCat = new Category("Foo");
        catDAO.makePersistent(newCat);

    }
}

Here I'm testing a data access object. Well, I'm not actually testing much as I don't assert any state after saving. I still have to port all my JUnit tests over but my first goal was to have the infrastructure ready.

The CategoryDAO is a stateless EJB that I wrote with the Generic DAO pattern. To make it an EJB I had to add a @Stateless annotation and get the EntityManager injected into the bean. What about transactions?

For a stateless EJB the default transaction attribute is REQUIRES, so any method I call on the DAO is executed in a system transaction. If several are called in the same test method they all run in the same transaction.

I've also started using TestNG groups, a great feature. By marking the test method as belonging to the group integration.database I can target it in my TestNG suite assembly in testng.xml:

<suite name="CaveatEmptor-EJB3" verbose="1">

    <test name="Runtime">
        <packages>
            <package name="org.hibernate.ce.auction.test.testng.runtime"/>
        </packages>
    </test>

    <test name="Integration">
        <groups>
            <run><include name="integration.*"/></run>
        </groups>
        <packages>
             <package name="org.hibernate.ce.auction.test.testng.persistence"/>
         </packages>
    </test>

</suite>

I still have to figure out how to create the assembly in a way that says use this runtime for this group of tests. Right now I'd need several suites for this.

Since I'm stuck with the old IntelliJ I've got to run the tests with Ant (I'd really like to have that green bar ;):

<target name="testng.run" depends="testng.package"
    description="TestNG tests with the JBoss EJB3 Microcontainer">
    <mkdir dir="${testng.out.dir}"/>

    <testng outputDir="${testng.out.dir}">
        <classpath>
            <pathelement path="${testng.build.dir}/${proj.shortname}.jar"/>
            <path refid="project.classpath"/>
            <path>
                <fileset dir="${container.lib}">
                    <include name="**/*.jar"/>
                </fileset>
            </path>
        </classpath>
        <xmlfileset dir="${testng.classes.dir}" includes="testng.xml"/>
    </testng>
</target>

So I'm hooked on TestNG now and will use it for all EJB tests in the future. I've got to check out Gavins TestNG configuration for JSF + EJB testing in Seam and how he simulates the HTTP environment...

Oh, and if any Jetbrains guys read this: fix the XML editor. There are people out there who use it to write docs. Thanks :)


Back to top