1 Reply Latest reply on Oct 14, 2010 5:31 AM by oranheim

    Unit testing an ActionListener

    oranheim

      Is there a way to test an ActionListener from TestNG?


      I'm using PrimeFaces with Seam and need to test the following:


      <p:commandButton value="Save" actionListener="#{unitProfile.saveOrUpdate}" />



      This call is using and ajax and it is not clear to me how to unit test AJAX calls from TestNG.




        • 1. Re: Unit testing an ActionListener
          oranheim

          I solved my issue and thought it might be interesting for others at the forum. The problem is to enable a way to test commandButton.actionListener events and make them testable through SeamTest. I didn't realize until yesterday that SeamTest doesn't constitute a viewRoot tree, where component state is being maintained. SeamTest obviously mocks what's important and relevant for enabling integration testing, and not full rendering. Consequently, you cannot make a call to viewRoot.findComponent('someClientId') and pass that reference to an ActionEvent, in which is further passed to the actionListeneer method event. So the remedy would be an MockActionEvent, as listed below.


          package com.company.test.mock;
          
          import javax.faces.component.UICommand;
          import javax.faces.component.html.HtmlForm;
          import javax.faces.context.FacesContext;
          import javax.faces.event.ActionEvent;
          
          public class MockActionEvent extends ActionEvent {
          
               private static final long serialVersionUID = 7181614850585837567L;
               
               private HtmlForm form;
          
               private UICommand button;
          
               public MockActionEvent(FacesContext facesContext, String formId, String buttonId) {
                    super(new UICommand());
                    
                    form = new HtmlForm();
                    form.setId(formId);
                    form.setPrependId(false);
                    
                    button = (UICommand) this.source;
                    button.setId(buttonId);
                    button.setParent(form);          
          
                    form.setParent(facesContext.getViewRoot());
               }
               
               public HtmlForm getForm() {
                    return form;
               }
          
               public UICommand getButton() {
                    return button;
               }
               
               public Object getAttribute(String key) {
                    return button.getAttributes().get(key);
               }
               
               public void setAttribute(String key, Object value) {
                    button.getAttributes().put(key, value);
               }
          
          }



          The MockActionEvent constitutes a form with a button added to it. You can store attribute values or save other data to the button, as applicable.


          From FacesRequest.invokeApplication() this simple snippet would be just enough to pass values between the calling code:



          MockActionEvent event = new MockActionEvent(getFacesContext(), "form1", "button1");
          event.setAttribute("person", getValue("#{person}"));
          beanAction.saveOrUpdate(event);
          



          ..and the action bean


          public void saveOrUpdate(ActionEvent event) {
               Person person = (Person) event.getComponent().getAttributes().get("person");
               person = em.merge(person);
          }
          



          This hasn't between thoroughly tested yet, so I don't know how well it works. But, it works for me.