Version 5

    Using Embedded JBoss with JUnit

     

    There are certain issues with Embedded JBoss that you have to worry about when doing unit testing.

     

    Bootstrap.bootstrap() and shutdown() can only be called once per JVM

     

    This is perhaps the biggest problem when using Embedded JBoss in unit testing.  There are various situations where you run unit tests:

     

    1. Running one test method in a particular class

    2. Running all tests in a particular class

    3. Running all test classes in your IDE that are within a directory structure

     

    How do you write your junit classes so that they can run in each of these scenarios?  Solving 1 is easy.  Just call bootstrap() and shutdown() in overriden setup() and tearDown() methods of your test class.  The problem with this approach is that you will no longer be able to run in situations 2 and 3 because junit instantiates, by default, an instance of each test class per test method.  In scenario 3, it becomes even worse because many IDE's will run all unit tests within the same JVM, thus you'll have the same exact problem.

     

    The solution is a simple one.  Call bootstrap() only once, and never call shutdown().  Here's how you might do it:

     

    
    public class MyTestCase extends TestCase
    {
    
    
       @Override
       protected void setUp() throws Exception
       {
          if (!Bootstrap.getInstance().isStarted()) Bootstrap.getInstance().bootstrap();
       }
    
    ...
    
    }
    
    

     

    How do I deploy something only once per test run?

     

    At first thought, deploying your embedded archives in the setUp() method and undeploying in tearDown() of your TestCases sounds like a good idea.  But what if you only want to deploy these archives once per test class?  Maybe deployment of any one of these archives takes a long time, or maybe you want to retain state between test method invocations.  To do this, you need to wrap your test class in a TestSetup, yet you want to write your test class so that in can run in scenarios 1-3 shown above.  Here's what you might do:

     

    public class EjbTestCase extends TestCase
    {
       private static boolean globalSetup = false;
    
       public static Test suite()
       {
          TestSuite suite = new TestSuite();
          suite.addTestSuite(EjbTestCase.class);
          globalSetup = true;
    
          return new TestSetup(suite)
          {
             @Override
             protected void setUp() throws Exception
             {
                super.setUp();
                if (!Bootstrap.getInstance().isStarted())
                {
                   Bootstrap.getInstance().bootstrap();
                }
                deploy();
             }
    
             @Override
             protected void tearDown() throws Exception
             {
                undeploy();
                if (System.getProperty("shutdown.embedded.jboss") != null) Bootstrap.getInstance().shutdown();
                super.tearDown();
             }
          };
       }
    
       @Override
       protected void setUp() throws Exception
       {
          if (globalSetup) return;
          Bootstrap.getInstance().bootstrap();
          deploy();
       }
    
       @Override
       protected void tearDown() throws Exception
       {
          if (globalSetup) return;
          undeploy();
       }
    
       private static AssembledDirectory jar;
    
       private static void deploy()
       {
          jar = AssembledContextFactory.getInstance().create("ejbTestCase.jar");
          jar.addClass(Customer.class);
          jar.addClass(CustomerDAOBean.class);
          jar.addClass(CustomerDAOLocal.class);
          jar.addClass(CustomerDAORemote.class);
          jar.mkdir("META-INF").addResource("tutorial-persistence.xml", "persistence.xml");
          try
          {
             Bootstrap.getInstance().deploy(jar);
          }
          catch (DeploymentException e)
          {
             throw new RuntimeException("Unable to deploy", e);
          }
       }
    
       private static void undeploy()
       {
          try
          {
             Bootstrap.getInstance().undeploy(jar);
             AssembledContextFactory.getInstance().remove(jar);
          }
          catch (DeploymentException e)
          {
             throw new RuntimeException("Unable to undeploy", e);
          }
       }
    
    
    

     

    The suite() method creates a TestSuite using a TestSetup that does Bootstrap().bootstrap() in its setUp() method as well as calls the static deploy() method.  This deploy method deploys the archive you are interested in deploying.  The overriden setUp() and tearDown() methods determine whether or not initialize happened within the suite() method, or whether an individual test method was run from your IDE.  If the test class had not been initialized, it calls Bootstrap.bootstrap() and the deploy() method.

     

    That's a lot of code to write.  Embedded JBoss provides a base test class to make it easier for you:  org.jboss.embedded.junit.BaseTestCase

     

    
    public class EasierEjbTestCase extends BaseTestCase
    {
       public static Test suite()
       {
          return preProcessedTest(EasierEjbTestCase.class);
       }
    
    
       public static void deploy()
       {
        ...
       }
    
       public static void undeploy()
       {
    ...
       }
    
    

     

    BaseTestCase creates the same template internally that we showed explicitly previously.  It looks to see if the test class has implemented a static deploy and/or undeploy method and calls those methods via reflection at the appropriate times.

     

     

    Bootstrap.bootstrap() takes 5-6 seconds.  (measured on a 2.4 ghz Core 2 platform)

     

    5-6 seconds isn't that long in the grand scheme of things, but starts to add up.  A common thing to do is to automate junit test runs by creating an Ant batchtest target within your build.xml file.  The problem with this scenario is that each test will bootstrap Embedded JBoss and your entire suite will run very slowly.  In this scenario, it is best to create a suite class that runs all the tests.

     

    public class AllTests extends TestSuite
    {
       public static void main(String[] args)
       {
          TestRunner.run(suite());
       }
    
       public static Test suite()
       {
          TestSuite suite = new TestSuite("All Tests");
    
          suite.addTest(EasierEjbTestCase.suite());
          suite.addTest(EjbTestCase.suite());
          suite.addTest(JarByResourceTestCase.suite());
          suite.addTest(MdbTestCase.suite());
    
          return new TestSetup(suite)
          {
             @Override
             protected void setUp() throws Exception
             {
                Bootstrap.getInstance().bootstrap();
                super.setUp();
             }
    
             @Override
             protected void tearDown() throws Exception
             {
                Bootstrap.getInstance().shutdown();
                super.tearDown();
             }
          };
    
       }