6 Replies Latest reply on May 13, 2007 3:32 PM by stu2

    Unit Testing EL in EJB-QL/HQL

    mrobinson28

      Is it possible to write a unit test for a method that uses EL expressions within EJB-QL/HQL? I attempted to do this and get the exception:

      javax.persistence.PersistenceException: org.hibernate.QueryException: unexpected char: '#'

      The documentation states that:

      Seam proxies the EntityManager or Session object whenever you use a Seam-managed persistence context or inject a container managed persistence context using @PersistenceContext.

      Does this not occur if the EntityManager is set using SeamTest and setField?

      Michael

        • 1. Re: Unit Testing EL in EJB-QL/HQL
          stu2

          I've been using SeamTest in integration mode (I'm not using setField for instance) and the em accepts seam parameters in EJB-QL.

          • 2. Re: Unit Testing EL in EJB-QL/HQL
            gavin.king

            If you're using integration tests, Seam will proxy the EM.

            If you're writing a unit test, and creating your own EM from an EMF, you need to do:

            EntityManager em = new EntityManagerProxy( emf.createEntityManager() );


            • 3. Re: Unit Testing EL in EJB-QL/HQL
              mrobinson28

              Ok, in my unit test I originally had:

              EntityManager entityManager = emf.createEntityManager();
              

              I have replaced it with:
              EntityManager entityManager = new EntityManagerProxy( emf.createEntityManager() );
              


              Now the exception is:
              java.lang.IllegalStateException: No application context active
               at org.jboss.seam.ScopeType.getContext(ScopeType.java:139)
               at org.jboss.seam.Component.getInstance(Component.java:1589)
               at org.jboss.seam.Component.getInstance(Component.java:1567)
               at org.jboss.seam.Component.getInstance(Component.java:1562)
               at org.jboss.seam.core.Expressions.instance(Expressions.java:219)
               at org.jboss.seam.persistence.QueryParser.<init>(QueryParser.java:60)
               at org.jboss.seam.persistence.QueryParser.<init>(QueryParser.java:33)
               at org.jboss.seam.persistence.EntityManagerProxy.createQuery(EntityManagerProxy.java:57)
              


              The class/method that I am attempting to unit test looks like:
              @Stateful
              @Scope(ScopeType.SESSION)
              @Name("userBrowser")
              @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
              public class UserBrowserAction implements Serializable, UserBrowser {
              
               @Logger
               private Log log;
              
               @PersistenceContext
               private EntityManager entityManager;
              
               @DataModel
               private List<User> users;
              
               @DataModelSelection(value = "users")
               private User selectedUser;
              
               @Factory("users")
               @Observer("newUserRegistered")
               @SuppressWarnings("unchecked")
               public void getUsers() {
               log.debug("Invoke factory for users");
              
               this.users = this.entityManager.createQuery("from User user where user.username <> #{currentUser.username} ").getResultList();
              
               log.debug("Found #0 users", this.users.size());
               }
              
               @Destroy
               @Remove
               public void destroy() {
               }
              }
              

              My unit test is:
              public class UserBrowserTest extends SeamTest {
              
               @Test (groups = "unit")
               @SuppressWarnings("unchecked")
               public void testGetUsers() throws Exception {
               EntityManagerFactory emf = Persistence.createEntityManagerFactory("test-persistence-unit");
               EntityManager entityManager = new EntityManagerProxy( emf.createEntityManager() );
              
               // create a log to inject
               Log log = Logging.getLog(UserBrowserAction.class);
              
               UserBrowser userBrowser = new UserBrowserAction();
              
               super.setField(userBrowser, "entityManager", entityManager);
               super.setField(userBrowser, "log", log);
              
               // invoke method
               userBrowser.getUsers();
              
               // inspect the property
               List<User> users = (List<User>) super.getField(userBrowser, "users");
              
               assert users != null;
               assert users.size() == 2;
               }
              }
              


              So my questions:

              • From the exception it looks like I may have to provide more than I am currently via something in org.jboss.seam.mock.* e.g currentUser (In the application currentUser is a session scoped component that is instantiated when a user logs in). Are there any examples of using these mock objects anywhere?
              • Currently I am extending SeamTest which I understand is used for integration testing. Is it incorrect to do this when unit testing; several of the methods provided seem helpful in unit testing also. Would it be feasible to override init so that the embedded container is not started for stand alone unit test (or maybe have something like SeamUnitTest?
              • In general I seem to be having difficulties writing unit test because it occurs over and over again that some method is accessing a variable that is within scope during normal program executions e.g. in the code above currentUser is within scope but is not injected into the component that is accessing it. Is this bad practice? I tested re-writing the class that I am trying to test to contain and instance of the currentUser object injected using @In and then accessing that object from the method that performs the query and this works, both in the application and in my unit test if I manually inject that object.
              • Is it possible to manually inject objects into different scopes programmatically for unit testing?


              • 4. Re: Unit Testing EL in EJB-QL/HQL
                zzzz8

                 

                "mrobinson28" wrote:

                So my questions:

                • From the exception it looks like I may have to provide more than I am currently via something in org.jboss.seam.mock.* e.g currentUser (In the application currentUser is a session scoped component that is instantiated when a user logs in). Are there any examples of using these mock objects anywhere?
                • Currently I am extending SeamTest which I understand is used for integration testing. Is it incorrect to do this when unit testing; several of the methods provided seem helpful in unit testing also. Would it be feasible to override init so that the embedded container is not started for stand alone unit test (or maybe have something like SeamUnitTest?
                • In general I seem to be having difficulties writing unit test because it occurs over and over again that some method is accessing a variable that is within scope during normal program executions e.g. in the code above currentUser is within scope but is not injected into the component that is accessing it. Is this bad practice? I tested re-writing the class that I am trying to test to contain and instance of the currentUser object injected using @In and then accessing that object from the method that performs the query and this works, both in the application and in my unit test if I manually inject that object.
                • Is it possible to manually inject objects into different scopes programmatically for unit testing?


                Did you ever figure out the answers to these excellent questions? I'm writing some unit tests and I'm running into some of your issues, especially the second issue (re: extending SeamTest for unit tests). I'm trying to avoid extending SeamTest in my unit tests. However, there's one nagging issue I have when doing this. Because the code I'm testing contains logging statements where the log object is injected using the @Logger annotation, TestNG always barfs with a null pointer exception when it encounters a logging statement in the code... Perhaps I can mock the log object. Any hints here?

                • 5. Re: Unit Testing EL in EJB-QL/HQL
                  matt.drees

                  Yeah, I mock the log object in my unit tests to get around the fact that there is no application context active. It looks like this, and seems to work ok:

                  package org.uscm.crs.misc;
                  
                  import org.jboss.seam.log.Log;
                  import org.jboss.seam.log.Logging;
                  
                  public class MockSeamLogger implements Log {
                  
                  
                   private Log log;
                  
                   public MockSeamLogger() {
                   log = Logging.getLog(MockSeamLogger.class);
                   }
                  
                   public MockSeamLogger(Class clazz) {
                   log = Logging.getLog(clazz);
                   }
                  
                  
                   private Object clean(Object object) {
                   if (object instanceof String) {
                   String string = (String) object;
                   return string.replace("#{", "{");
                   }
                   return object;
                   }
                  
                   public void debug(Object object, Object... params) {
                   log.debug(clean(object), params);
                   }
                  
                  
                   public void debug(Object object, Throwable t, Object... params) {
                   log.debug(clean(object), t, params);
                  
                   }
                  
                   public void error(Object object, Object... params) {
                   log.error(clean(object), params);
                   }
                  
                   public void error(Object object, Throwable t, Object... params) {
                   log.error(clean(object), t, params);
                  
                   }
                  
                   public void fatal(Object object, Object... params) {
                   log.fatal(clean(object), params);
                   }
                  
                   public void fatal(Object object, Throwable t, Object... params) {
                   log.fatal(clean(object), t, params);
                  
                   }
                  
                   public void info(Object object, Object... params) {
                   log.info(clean(object), params);
                   }
                  
                   public void info(Object object, Throwable t, Object... params) {
                   log.info(clean(object), t, params);
                  
                   }
                  
                   public boolean isDebugEnabled() {
                   return true;
                   }
                  
                   public boolean isErrorEnabled() {
                   return true;
                   }
                  
                   public boolean isFatalEnabled() {
                   return true;
                   }
                  
                   public boolean isInfoEnabled() {
                   return true;
                   }
                  
                   public boolean isTraceEnabled() {
                   return true;
                   }
                  
                   public boolean isWarnEnabled() {
                   return true;
                   }
                  
                   public void trace(Object object, Object... params) {
                   log.trace(clean(object), params);
                   }
                  
                   public void trace(Object object, Throwable t, Object... params) {
                   log.trace(clean(object), t, params);
                   }
                  
                   public void warn(Object object, Object... params) {
                   log.warn(clean(object), params);
                   }
                  
                   public void warn(Object object, Throwable t, Object... params) {
                   log.warn(clean(object), t);
                   }
                  
                  }
                  


                  I'm definitely interested to hear if anyone else has found useful patterns for unit testing. I'm wondering if it'd be worth it to simply set up the Application context manually; it might be lightweight enough to be reasonable in a unit test.

                  • 6. Re: Unit Testing EL in EJB-QL/HQL
                    stu2

                    Well injection of logs is only saving you this:

                    public Log log = Logging.getLog(RegisterAndLoginTest.class);
                    


                    I use this outside of SFSBs where unit testing is important.