11 Replies Latest reply on Sep 1, 2006 4:00 PM by Maurice Zeijen

    How do I test simple SLSBs?

    Chuck Adams Novice

      I'm trying to do the simplest possible test, and have a stateless bean called GreeterBean, with its @Name set to greeter, with a getHello() method that returns "Hello, world!", so I can plug it into a page as #{greeter.hello}. This works perfectly. My troubles come when trying to write a test for it.

      public class GreeterTest extends SeamTest {
      
       @Test
       public void testGreeterHello() {
       Greeter g = (Greeter) Component.getInstance("greeter", true);
       assert g.getHello().equals("Hello, world!");
       }
      }
      


      Unfortunately, this fails, and I don't mean the test, but Component.getInstance dies with a NPE because Contexts.getApplicationContext() returns null. I notice there's a MockApplication class lying around, but I'm stumped on how to actually use it, and the docs are rather sparse on the subject.

      I tried dispensing with mocks and just going with embedded ejb3, but that's been a frightful experience, also replete with java's favorite catch-all error, the NPE (I curse the very invention of null). I found that I could make my embedded ejb3 test work if I commented out @Name, but obviously this doesn't constitute much of a test. I don't have the TB handy at the moment since I've deleted my embedded ejb3 launch configuration, but IIRC, it was croaking somewhere in the PostConstruct method of some long interceptor call chain.


        • 1. Re: How do I test simple SLSBs?
          Maurice Zeijen Master

          I am also pretty new to this stuff, but here is some info to probably can help you. I got it working in eclipse.

          I think that If you want use TestNG to test your stuff in a Seam/EJB3 environment then you must write the tests as in the registration example:

          
          package org.jboss.seam.example.registration.test;
          
          import org.jboss.seam.Component;
          import org.jboss.seam.example.registration.Register;
          import org.jboss.seam.example.registration.User;
          import org.jboss.seam.mock.SeamTest;
          import org.testng.annotations.Test;
          
          public class RegisterTest extends SeamTest
          {
          
           @Test
           public void testLogin() throws Exception
           {
          
           new Script() {
          
           @Override
           protected void updateModelValues() throws Exception
           {
           User user = (User) Component.getInstance("user", true);
           assert user!=null;
           user.setUsername("1ovthafew");
           user.setPassword("secret");
           user.setName("Gavin King");
           }
          
           @Override
           protected void invokeApplication()
           {
           Register register = (Register) Component.getInstance("register", true);
           String outcome = register.register();
           assert "/registered.jsp".equals( outcome );
           }
          
           @Override
           protected void renderResponse()
           {
           User user = (User) Component.getInstance("user", false);
           assert user!=null;
           assert user.getName().equals("Gavin King");
           assert user.getUsername().equals("1ovthafew");
           assert user.getPassword().equals("secret");
           }
          
           }.run();
          
           }
          
          }
          
          


          Take a special look at the class override of new Script() { ... }. It simulates a complete request loop.

          You must make sure that you configured your TestNG for an Embedded Seam application because it needs to run with the JBoss micro container.

          The extra configuration files you need are:
          - default.persistence.properties
          - ejb3-interceptors-aop.xml
          - embedded-jboss-beans.xml
          - jndi.properties
          - log4j.xml

          In your components.xml will probably look something like this:

          
          <components>
          
           <component name="org.jboss.seam.core.init">
           <property name="debug">true</property>
           <property name="myFacesLifecycleBug">@myFacesLifecycleBug@</property>
           <property name="jndiPattern">@jndiPattern@</property>
           </component>
          
           <!-- 120 second conversation timeout -->
           <component name="org.jboss.seam.core.manager">
           <property name="conversationTimeout">120000</property>
           </component>
          
           <component class="org.jboss.seam.core.Ejb" installed="@embeddedEjb@"/>
          
          </components>
          
          


          Use the components.properties file to set the following properties of the component.xml (those tags with @):

          
          myFacesLifecycleBug : false
          embeddedEjb : true
          jndiPattern : #{ejbName}/local
          
          


          I eclipse I have a seperate folder for all those embedded configuration. I add it to the classpath when I run TestNG.

          Good luck

          • 2. Re: How do I test simple SLSBs?
            Chuck Adams Novice

            I looked at SeamTest and felt kind of silly for not putting any of the test logic in a script. I changed it thusly:

            public class GreeterTest extends SeamTest {
            
             @Test
             public void testGreeterHello() throws Exception {
             new Script() {
             @Override
             protected void invokeApplication() {
             Greeter g = (Greeter) Component.getInstance("greeter");
            
             assert "Hello, world!".equals(g.getHello());
             }
             }.run();
             }
            }
            


            And at least I got a more familiar error.


            
            org.jboss.seam.InstantiationException: Could not instantiate Seam component: greeter
             at org.jboss.seam.Component.newInstance(Component.java:1440)
             at org.jboss.seam.Component.getInstance(Component.java:1350)
             at org.jboss.seam.Component.getInstance(Component.java:1324)
             at org.jboss.seam.Component.getInstance(Component.java:1318)
             at bizint.fiat.test.GreeterTest$1.invokeApplication(GreeterTest.java:18)
             at org.jboss.seam.mock.SeamTest$Script.run(SeamTest.java:242)
             at bizint.fiat.test.GreeterTest.testGreeterHello(GreeterTest.java:22)
            Caused by: javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
             at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:645)
             at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:288)
             at javax.naming.InitialContext.getURLOrDefaultInitCtx(InitialContext.java:325)
             at javax.naming.InitialContext.lookup(InitialContext.java:392)
             at org.jboss.seam.Component.instantiate(Component.java:809)
             at org.jboss.seam.Component.newInstance(Component.java:1436)
             ... 23 more
            ... Removed 17 stack frames
            


            I've always wondered at the generality of "could not instantiate Seam component". Why not? Was the underlying class not found? Name not resolved? Bad Mood? Stars not in alignment? Doesn't seem to be name related, because when I try Component.getInstance("kwyjibo") I get the ubuquitous NullPointerException instead (Java seems to give me more of those than I ever got in C or C++)

            • 3. Re: How do I test simple SLSBs?
              Chuck Adams Novice

              I need to stop skipping lunch before I post, and I wish I could edit my posts ... hard to get any more specific than "Need to specify class name in environment or syst
              em property, or as an applet parameter, or in an application resource file". I was hoping that I wouldn't need an actual container, but I guess there's no getting around the need for it when I'm testing actual EJB's.

              Maybe I should switch my app to POJOs and get rid of some the layers causing this pain and suffering. Maybe I can even plug seam in to db4o ;)

              • 4. Re: How do I test simple SLSBs?
                Gavin King Master

                The only container you need is embeddable ejb3. Look at some more example applications and you'll see how easy it is to do this testing even with EJB3 components.

                • 5. The microcontainer is not scanning the correct location on t
                  [Si]dragon Newbie

                  I have a brief section on this in a wiki I am putting together to document these issues. See the section linked here: http://sidragon.net/wiki/JBoss_Seam_test_environment#Component_and_deployment_descriptor_locations

                  This gist is that it appears that you must have your EJB3 and Seam deployment descriptors in the same location as your component classes. Otherwise, the location never gets scanned. It would be extremely nice if there was a way to override this behavior.

                  While I'm posting, does anyone have a suggestion for this problem: http://sidragon.net/wiki/JBoss_Seam_test_environment#Missing_FacesMessages_instance

                  • 6. Re: How do I test simple SLSBs?
                    Chuck Adams Novice

                    A null test using the embedded-ejb3 in seam results in this result from EJB3StandaloneBootstrap.boot(null):

                    And no, my tests are not parallel.


                    11:59:39,223 ERROR [AbstractKernelController] Error installing to Start: name=TransactionManagerInitializer state=Create
                    java.lang.NoSuchFieldError: xidFactory
                     at org.jboss.tm.TransactionManagerInitializer.start(TransactionManagerInitializer.java:110)
                     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
                     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
                     at java.lang.reflect.Method.invoke(Method.java:589)
                     at org.jboss.reflect.plugins.introspection.ReflectionUtils.invoke(ReflectionUtils.java:55)
                     at org.jboss.reflect.plugins.introspection.ReflectMethodInfoImpl.invoke(ReflectMethodInfoImpl.java:107)
                     at org.jboss.joinpoint.plugins.BasicMethodJoinPoint.dispatch(BasicMethodJoinPoint.java:66)
                     at org.jboss.kernel.plugins.dependency.KernelControllerContextActions.dispatchJoinPoint(KernelControllerContextActions.java:100)
                     at org.jboss.kernel.plugins.dependency.KernelControllerContextActions$LifecycleAction.installAction(KernelControllerContextActions.java:582)
                     at org.jboss.kernel.plugins.dependency.KernelControllerContextActions$KernelControllerContextAction.install(KernelControllerContextActions.java:175)
                     at org.jboss.dependency.plugins.AbstractControllerContextActions.install(AbstractControllerContextActions.java:51)
                     at org.jboss.dependency.plugins.AbstractControllerContext.install(AbstractControllerContext.java:226)
                     at org.jboss.dependency.plugins.AbstractController.install(AbstractController.java:593)
                     at org.jboss.dependency.plugins.AbstractController.incrementState(AbstractController.java:346)
                     at org.jboss.dependency.plugins.AbstractController.resolveContexts(AbstractController.java:438)
                     at org.jboss.dependency.plugins.AbstractController.resolveContexts(AbstractController.java:379)
                     at org.jboss.dependency.plugins.AbstractController.install(AbstractController.java:225)
                     at org.jboss.dependency.plugins.AbstractController.install(AbstractController.java:151)
                     at org.jboss.kernel.plugins.deployment.AbstractKernelDeployer.deployBean(AbstractKernelDeployer.java:291)
                     at org.jboss.kernel.plugins.deployment.AbstractKernelDeployer.deployBeans(AbstractKernelDeployer.java:261)
                     at org.jboss.kernel.plugins.deployment.AbstractKernelDeployer.deploy(AbstractKernelDeployer.java:117)
                     at org.jboss.kernel.plugins.deployment.xml.BeanXMLDeployer.deploy(BeanXMLDeployer.java:95)
                     at org.jboss.ejb3.embedded.EJB3StandaloneBootstrap.boot(EJB3StandaloneBootstrap.java:380)
                     at bizint.fiat.test.AbstractEJB3Test.startupEmbeddedJboss(AbstractEJB3Test.java:17)
                     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
                     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
                     at java.lang.reflect.Method.invoke(Method.java:589)
                     at org.testng.internal.MethodHelper.invokeMethod(MethodHelper.java:552)
                     at org.testng.internal.Invoker.invokeConfigurationMethod(Invoker.java:322)
                     at org.testng.internal.Invoker.invokeConfigurations(Invoker.java:156)
                     at org.testng.SuiteRunner.privateRun(SuiteRunner.java:223)
                     at org.testng.SuiteRunner.run(SuiteRunner.java:145)
                     at org.testng.TestNG.createAndRunSuiteRunners(TestNG.java:901)
                     at org.testng.TestNG.runSuitesLocally(TestNG.java:863)
                     at org.testng.TestNG.run(TestNG.java:613)
                     at org.testng.eclipse.runner.RemoteTestNG.run(RemoteTestNG.java:85)
                     at org.testng.eclipse.runner.RemoteTestNG.main(RemoteTestNG.java:127)
                    Creating C:\workspace\FiatEJB\test-output\Fiat\All.html
                    FAILED: startupEmbeddedJboss
                    java.lang.RuntimeException: java.lang.IllegalArgumentException: Null name
                     at org.jboss.ejb3.embedded.EJB3StandaloneBootstrap.boot(EJB3StandaloneBootstrap.java:391)
                     at bizint.fiat.test.AbstractEJB3Test.startupEmbeddedJboss(AbstractEJB3Test.java:17)
                    Caused by: java.lang.IllegalArgumentException: Null name
                     at org.jboss.dependency.plugins.AbstractController.getContext(AbstractController.java:101)
                     at org.jboss.kernel.plugins.dependency.AbstractKernelController.getContext(AbstractKernelController.java:94)
                     at org.jboss.kernel.plugins.deployment.AbstractKernelDeployer.internalValidate(AbstractKernelDeployer.java:229)
                     at org.jboss.kernel.plugins.deployment.AbstractKernelDeployer.validate(AbstractKernelDeployer.java:161)
                     at org.jboss.ejb3.embedded.EJB3StandaloneBootstrap.boot(EJB3StandaloneBootstrap.java:381)
                     ... 15 more
                    ... Removed 14 stack frames
                    
                    



                    • 7. Re: How do I test simple SLSBs?
                      Chuck Adams Novice

                      I switched my test's classpath to use a freshly installed embedded-ejb3 download's libraries and conf/ directory, and this particular error went away. The version of embedded ejb3 shipping with seam would appear to be broken, and it appears to be the libs, since only using the conf/ directory of the non-seam version was to no avail -- I had to put the non-seam version's libs in the front of the classpath.

                      Of course, I still get the same errors, they now just take a lot longer to show up. I'll see if I can reproduce the error I get when I test it as a regular EJB session bean and leave the @Name annotation (when I remove it, it works fine perfectly). No time right now unfortunately...

                      I recall some post elsewhere in jbossland about there being some fiddliness in the embedded container when J2EE 1.4 and JEE 5 libs are both in the classpath. Eclipse WTP is indeed adding J2EE 1.4 junk in the "JBOSS 4.0" server libs, that I never use (I can't seem to get rid of them though).

                      As for the examples, I can't find anything like a simple unit test, just rather complex JSF-level integration tests. All well and good, but I wanted to start with units, since my app is going to be mostly fronted with service-like session bean facades anyway. It's also proving a real pain to extricate the tests from the dependencies on the rest of the seam installation (though I really do appreciate that the examples aren't littered with redundant jars just to make them totally standalone)

                      • 8. Re: How do I test simple SLSBs?
                        Chuck Adams Novice

                        My informed hunch about Eclipse was right. Once I fixed the test's classpath and removed the extra cruft, the container started up fine. Deployment's not as fast with this thing as I would have hoped, but I can live with it.

                        I'm still having jndi problems now.

                        org.jboss.seam.InstantiationException: Could not instantiate Seam component: greeter
                         at org.jboss.seam.Component.newInstance(Component.java:1440)
                         at org.jboss.seam.Component.getInstance(Component.java:1350)
                         at org.jboss.seam.Component.getInstance(Component.java:1324)
                         at org.jboss.seam.Component.getInstance(Component.java:1318)
                         at bizint.fiat.test.GreeterTest$1.invokeApplication(GreeterTest.java:25)
                         at org.jboss.seam.mock.SeamTest$Script.run(SeamTest.java:242)
                         at bizint.fiat.test.GreeterTest.testGreeterHello(GreeterTest.java:29)
                        Caused by: javax.naming.NameNotFoundException: "Fiat not bound
                         at org.jnp.server.NamingServer.getBinding(NamingServer.java:529)
                         at org.jnp.server.NamingServer.getBinding(NamingServer.java:537)
                         at org.jnp.server.NamingServer.getObject(NamingServer.java:543)
                         at org.jnp.server.NamingServer.lookup(NamingServer.java:267)
                         at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:626)
                         at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:588)
                         at javax.naming.InitialContext.lookup(InitialContext.java:392)
                         at org.jboss.seam.Component.instantiate(Component.java:809)
                         at org.jboss.seam.Component.newInstance(Component.java:1436)
                         ... 23 more
                        


                        Is there something special I need to do with jndi.properties, seam.properties, components.properties, etc?

                        Here is my seam.properties. The docs say it can be empty, but if I leave it empty, seam screams at me to set it or use @JndiName, and won't proceed.

                        org.jboss.seam.core.init.jndiPattern = "Fiat/#{ejbName}/local"
                        


                        Here is my components.properties. I've always marvelled at the inconsistency between these two instances of the same property, jndiPattern, but I've never even made a hello world app work without this inconsistency.

                        myFacesLifecycleBug : false
                        embeddedEjb : true
                        jndiPattern : #{ejbName}/local
                        


                        And here is my components.xml

                        <components>
                        
                         <component name="org.jboss.seam.core.init">
                         <property name="debug">true</property>
                         <property name="myFacesLifecycleBug">@myFacesLifecycleBug@</property>
                         <property name="jndiPattern">@jndiPattern@</property>
                         </component>
                        
                         <!-- 120 second conversation timeout -->
                         <component name="org.jboss.seam.core.manager">
                         <property name="conversationTimeout">120000</property>
                         </component>
                        
                         <component class="org.jboss.seam.core.Ejb" installed="@embeddedEjb@"/>
                        </components>
                        


                        These are all in a folder called test-inf, which is first in my test's classpath. I don't believe they're even really being looked at however, since I put syntax errors in components.xml and there wasn't even a peep of warning or error.


                        • 9. Re: How do I test simple SLSBs?
                          Maurice Zeijen Master


                          In the embedded jboss container the jndi path should be: "#{ejbName}/local". But because you defined the jndi "Fiat/#{ejbName}/local" in your seam.properties Seam can't find the object in the jndi.

                          Do you have the components.xml in your WEB-INF directory? Because else Seam can't find it (I think..., I got it working that way). Also make sure that the right components.properties (with the "jndiPattern : #{ejbName}/local") is on the classpath of your test run.

                          Good luck...

                          • 10. Re: How do I test simple SLSBs?
                            Chuck Adams Novice

                             

                            Do you have the components.xml in your WEB-INF directory?


                            That did it, now it's working -- You sir are the savior of my sanity :D



                            • 11. Re: How do I test simple SLSBs?
                              Maurice Zeijen Master

                              no problem ;)