2 Replies Latest reply on Oct 19, 2006 2:25 AM by Markus Dahm

    Seam and JUnit

    Markus Dahm Novice

      Hi,

      I tried to get Seam tests running with JUnit. As a first approach I took one
      of the NG-Tests from the examples and tried to adapt it:

      
      public class SeamTest extends TestCase {
      
       private MockExternalContext externalContext;
       private MockServletContext servletContext;
       private MockApplication application;
       private SeamPhaseListener phases;
       private MockFacesContext facesContext;
       private MockHttpSession session;
       private Map<String, ConversationState> conversationStates;
      
       protected void setUp() throws Exception {
       init();
       begin();
       }
      
       protected void tearDown() throws Exception {
       end();
       cleanup();
       }
      
       public void testDefault() 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.setRealName("Gavin King");
       }
      
       @Override
       protected void invokeApplication()
       {
       LoginAction register = (LoginAction) Component.getInstance("login", true);
       String outcome = register.login();
       assert "success".equals( outcome );
       }
      
       @Override
       protected void renderResponse()
       {
       User user = (User) Component.getInstance("user", false);
       assert user!=null;
       assert user.getRealName().equals("Gavin King");
       assert user.getUserName().equals("1ovthafew");
       assert user.getPassword().equals(null);
       }
      
       }.run();
       }
      
       protected HttpSession getSession()
       {
       return (HttpSession) externalContext.getSession(true);
       }
      
       protected boolean isSessionInvalid()
       {
       return ( (MockHttpSession) getSession() ).isInvalid();
       }
      
       protected FacesContext getFacesContext()
       {
       return facesContext;
       }
      
       /**
       * Script is an abstract superclass for usually anonymous
       * inner classes that test JSF interactions.
       *
       * @author Gavin King
       */
       public abstract class Script
       {
       private String conversationId;
       private String outcome;
       private boolean validationFailed;
      
       /**
       * A script for a JSF interaction with
       * no existing long-running conversation.
       */
       protected Script() {}
      
       /**
       * A script for a JSF interaction in the
       * scope of an existing long-running
       * conversation.
       */
       protected Script(String id)
       {
       conversationId = id;
       }
      
       protected boolean isGetRequest()
       {
       return false;
       }
      
       protected String getViewId()
       {
       return null;
       }
      
       /**
       * Helper method for resolving components in
       * the test script.
       */
       protected Object getInstance(Class clazz)
       {
       return Component.getInstance(clazz, true);
       }
      
       /**
       * Helper method for resolving components in
       * the test script.
       */
       protected Object getInstance(String name)
       {
       return Component.getInstance(name, true);
       }
      
       /**
       * Override to implement the interactions between
       * the JSF page and your components that occurs
       * during the apply request values phase.
       */
       protected void applyRequestValues() throws Exception {}
       /**
       * Override to implement the interactions between
       * the JSF page and your components that occurs
       * during the process validations phase.
       */
       protected void processValidations() throws Exception {}
       /**
       * Override to implement the interactions between
       * the JSF page and your components that occurs
       * during the update model values phase.
       */
       protected void updateModelValues() throws Exception {}
       /**
       * Override to implement the interactions between
       * the JSF page and your components that occurs
       * during the invoke application phase.
       */
       protected void invokeApplication() throws Exception {}
       protected void setOutcome(String outcome) { this.outcome = outcome; }
       protected String getInvokeApplicationOutcome() { return outcome; }
       /**
       * Override to implement the interactions between
       * the JSF page and your components that occurs
       * during the render response phase.
       */
       protected void renderResponse() throws Exception {}
       /**
       * Override to set up any request parameters for
       * the request.
       */
       protected void setup() {}
      
       public Map<String, String[]> getParameters()
       {
       return ( (MockHttpServletRequest) externalContext.getRequest() ).getParameters();
       }
      
       public Map<String, String[]> getHeaders()
       {
       return ( (MockHttpServletRequest) externalContext.getRequest() ).getHeaders();
       }
      
       protected void validate(Class modelClass, String property, Object value)
       {
       ClassValidator validator = Validation.getValidator(modelClass);
       InvalidValue[] ivs = validator.getPotentialInvalidValues(property, value);
       if (ivs.length>0)
       {
       validationFailed = true;
       FacesMessage message = FacesMessages.createFacesMessage( FacesMessage.SEVERITY_WARN, ivs[0].getMessage() );
       FacesContext.getCurrentInstance().addMessage( property, /*TODO*/ message );
       FacesContext.getCurrentInstance().renderResponse();
       }
       }
      
       public boolean isValidationFailure()
       {
       return validationFailed;
       }
      
       @SuppressWarnings("unchecked")
       public String run() throws Exception
       {
       externalContext = new MockExternalContext(servletContext, session);
       facesContext = new MockFacesContext( externalContext, application );
       facesContext.setCurrent();
       Map attributes = facesContext.getViewRoot().getAttributes();
      
       if ( !isGetRequest() && conversationId!=null )
       {
       if ( conversationStates.containsKey( conversationId ) )
       {
       attributes.putAll(
       conversationStates.get( conversationId ).state
       );
       }
       }
      
       setup();
      
       facesContext.getViewRoot().setViewId( getViewId() );
      
       phases.beforePhase( new PhaseEvent(facesContext, PhaseId.RESTORE_VIEW, MockLifecycle.INSTANCE) );
       phases.afterPhase( new PhaseEvent(facesContext, PhaseId.RESTORE_VIEW, MockLifecycle.INSTANCE) );
      
       if ( !isGetRequest() )
       {
      
       phases.beforePhase( new PhaseEvent(facesContext, PhaseId.APPLY_REQUEST_VALUES, MockLifecycle.INSTANCE) );
      
       applyRequestValues();
      
       phases.afterPhase( new PhaseEvent(facesContext, PhaseId.APPLY_REQUEST_VALUES, MockLifecycle.INSTANCE) );
       phases.beforePhase( new PhaseEvent(facesContext, PhaseId.PROCESS_VALIDATIONS, MockLifecycle.INSTANCE) );
      
       processValidations();
      
       phases.afterPhase( new PhaseEvent(facesContext, PhaseId.PROCESS_VALIDATIONS, MockLifecycle.INSTANCE) );
      
       if ( !FacesContext.getCurrentInstance().getRenderResponse() )
       {
      
       phases.beforePhase( new PhaseEvent(facesContext, PhaseId.UPDATE_MODEL_VALUES, MockLifecycle.INSTANCE) );
      
       updateModelValues();
      
       phases.afterPhase( new PhaseEvent(facesContext, PhaseId.UPDATE_MODEL_VALUES, MockLifecycle.INSTANCE) );
       phases.beforePhase( new PhaseEvent(facesContext, PhaseId.INVOKE_APPLICATION, MockLifecycle.INSTANCE) );
      
       invokeApplication();
      
       String outcome = getInvokeApplicationOutcome();
       facesContext.getApplication().getNavigationHandler().handleNavigation(facesContext, null, outcome);
      
       phases.afterPhase( new PhaseEvent(facesContext, PhaseId.INVOKE_APPLICATION, MockLifecycle.INSTANCE) );
      
       }
      
       }
      
       if ( !FacesContext.getCurrentInstance().getResponseComplete() )
       {
      
       phases.beforePhase( new PhaseEvent(facesContext, PhaseId.RENDER_RESPONSE, MockLifecycle.INSTANCE) );
      
       //TODO: hackish workaround for the fact that page actions don't get called!
       if ( isGetRequest() )
       {
       Lifecycle.setPhaseId(PhaseId.INVOKE_APPLICATION);
       invokeApplication();
       Lifecycle.setPhaseId(PhaseId.RENDER_RESPONSE);
       }
      
       renderResponse();
      
       facesContext.getApplication().getStateManager().saveSerializedView(facesContext);
      
       phases.afterPhase( new PhaseEvent(facesContext, PhaseId.RENDER_RESPONSE, MockLifecycle.INSTANCE ) );
      
       }
      
       Map pageContextMap = (Map) attributes.get( ScopeType.PAGE.getPrefix() );
       if (pageContextMap!=null)
       {
       conversationId = (String) pageContextMap.get(Manager.CONVERSATION_ID);
       ConversationState conversationState = new ConversationState();
       conversationState.state.putAll( attributes );
       conversationStates.put( conversationId, conversationState );
       }
      
       return conversationId;
       }
      
       }
      
       public void begin()
       {
       session = new MockHttpSession(servletContext);
       }
      
       public void end()
       {
       Lifecycle.endSession( servletContext, new ServletSessionImpl(session) );
       session = null;
       }
      
       /**
       * Create a SeamPhaseListener by default. Override to use
       * one of the other standard Seam phase listeners.
       */
       protected SeamPhaseListener createPhaseListener()
       {
       return new SeamPhaseListener();
       }
      
       public void init() throws Exception
       {
       application = new MockApplication();
       application.setStateManager( new SeamStateManager( application.getStateManager() ) );
       application.setNavigationHandler( new SeamNavigationHandler( application.getNavigationHandler() ) );
       //don't need a SeamVariableResolver, because we don't test the view
       phases = createPhaseListener();
      
       servletContext = new MockServletContext();
       initServletContext( servletContext.getInitParameters() );
       new Initialization(servletContext).init();
       Lifecycle.setServletContext(servletContext);
      
       conversationStates = new HashMap<String, ConversationState>();
       }
      
       public void cleanup() throws Exception
       {
       Lifecycle.endApplication(servletContext);
       externalContext = null;
       conversationStates.clear();
       conversationStates = null;
       }
      
       /**
       * Override to set up any servlet context attributes.
       */
       public void initServletContext(Map initParams) {}
      
       private class ConversationState
       {
       Map state = new HashMap();
       }
      
       protected InitialContext getInitialContext() throws NamingException {
       return Naming.getInitialContext();
       }
      
       protected UserTransaction getUserTransaction() throws NamingException
       {
       return Transactions.getUserTransaction();
       }
      
       protected Object getField(Object object, String fieldName)
       {
       try
       {
       Field declaredField = object.getClass().getDeclaredField(fieldName);
       if ( !declaredField.isAccessible() ) declaredField.setAccessible(true);
       return declaredField.get(object);
       }
       catch (Exception e)
       {
       throw new IllegalArgumentException("could not get field value: " + fieldName, e);
       }
       }
      
       protected void setField(Object object, String fieldName, Object value)
       {
       try
       {
       Field declaredField = object.getClass().getDeclaredField(fieldName);
       if ( !declaredField.isAccessible() ) declaredField.setAccessible(true);
       declaredField.set(object, value);
       }
       catch (Exception e)
       {
       throw new IllegalArgumentException("could not set field value: " + fieldName, e);
       }
       }
      }
      


      I'm not sure what's wrong with it, all I get is:

      java.lang.AssertionError
       at de.kvbb.ingver.test.SeamTest$1.updateModelValues(SeamTest.java:72)
       at de.kvbb.ingver.test.SeamTest$Script.run(SeamTest.java:280)
       at de.kvbb.ingver.test.SeamTest.testDefault(SeamTest.java:96)
       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:585)
       at junit.framework.TestCase.runTest(TestCase.java:154)
       at junit.framework.TestCase.runBare(TestCase.java:127)
       at junit.framework.TestResult$1.protect(TestResult.java:106)
       at junit.framework.TestResult.runProtected(TestResult.java:124)
       at junit.framework.TestResult.run(TestResult.java:109)
       at junit.framework.TestCase.run(TestCase.java:118)
       at junit.framework.TestSuite.runTest(TestSuite.java:208)
       at junit.framework.TestSuite.run(TestSuite.java:203)
       at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:478)
       at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:344)
       at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
      


      Thanks in advance
      Markus

        • 1. Re: Seam and JUnit
          Daniel Young Novice

          Hi Markus,

          I extended the SeamTest class for JUnit4 and it works fine. However, to be honest, you may find moving to TestNG is preferable, I'm still tossing it up.

          Here's what I hacked out very quickly, I overrode the begin method to set up my em and tm, so that I could use those to set up test data in my setUp(). If you don't need that, maybe you can even subclass SeamTest directly.

          public class BaseSeamTest extends SeamTest {
           protected EntityManager em;
           protected TransactionManager tm;
          
          
           @Override
           public void begin() {
           super.begin();
           if (em == null) {
           try {
           em = (EntityManager) getInitialContext().lookup("java:/EntityManagers/localUnitTestEM");
           tm = (TransactionManager) getInitialContext().lookup("java:/TransactionManager");
           } catch (NamingException e) {
           throw new RuntimeException(e);
           }
           }
           }
          }
          
          
          public class LoginTest extends BaseSeamTest {
          
           @Test
           public void testFailedLogin() throws Exception {
           new Script() {
           @Override
           protected void updateModelValues() throws Exception {
           assertFalse(isSessionInvalid());
           final User user = (User) Component.getInstance("user", true);
           assertNotNull(user);
           user.setLogin("LoginTest_testLogin");
           user.setPassword("password");
           }
          
           @Override
           protected void invokeApplication() throws Exception {
           Login login = (Login) Component.getInstance("login", true);
           assertEquals(null, login.login());
           }
          
           @Override
           protected void renderResponse() throws Exception {
           final Iterator messages = FacesContext.getCurrentInstance().getMessages();
           final FacesMessage message = (FacesMessage) messages.next();
           assertEquals("LoginAction_InvalidLogin", message.getDetail());
           assertFalse(messages.hasNext());
           }
           }.run();
           }
          .......
          


          I hope that helps,

          Daniel.


          • 2. Re: Seam and JUnit
            Markus Dahm Novice

            Thanks a lot, I'll give it a try

            Cheers
            Markus