With the latest release of the Arquillian Portal Extension, 1.1.0.Alpha1, we can now use Arquillian Warp to test the server side behavior of our JSF portlets! I'm very excited by this as it has been something I've wanted, and worked to achieve, for a while now.

 

To be able to take advantage of all this goodness, we need to add the Portlet extension for Warp to our build:

 

<dependency>
<groupId>org.jboss.arquillian.extension</groupId>
<artifactId>arquillian-portal-warp-jsf</artifactId>
<version>1.1.0.Alpha1</version>
</dependency>



 

Currently this version of the Arquillian Portal Extension will bring with it Arquillian Warp 1.0.0.Alpha5. When a new version is released, it would be possible to take advantage of the new features it provides by directly importing the Warp BOM ahead of the above dependency. Of course that is providing that no Warp APIs changed which result in the Portal extension to break!

 

Ok, so lets create a JSF portlet to test this puppy out!

 

Here's a simple JSF page we can use:

<f:view xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xml:lang="en" lang="en">
<h:head></h:head>
<h:body>
   <h:form>
     <h:inputText id="input" value="#{bean.text}" />
     <h:commandButton id="submit" value="Ok" action="#{bean.action}" />
   </h:form>
</h:body>
</f:view>



 

And the JSF bean that it uses:

@ManagedBean
@RequestScoped
public class Bean {
private String text = "original";

  public String getText() { return text; }
  public void setText(String value) { this.text = value; }

  public String action() {
    return null;
  }
}



 

We have a simple form with one field, that doesn't really do much, but we don't need it to to show the benefit of Warp and JSF portlets.

 

Now the fun part, writing the test!

 

Annotated on the test class we need to specify we're using Arquillian, Warp, and the Portal extension. For this we need:

@RunWith(Arquillian.class)
@WarpTest
@PortalTest



 

Obviously you need to define the @Deployment method, including our bean and JSF page into the test archive, along with Portlet Bridge:

@Deployment
public static PortletArchive createDeployment() {
  PortletArchive archive = ShrinkWrap.create(PortletArchive.class, "JSFPortletWarp.war");

  File[] libs = Maven.resolver()
                .loadPomFromFile("pom.xml")
                .resolve("org.jboss.portletbridge:portletbridge-impl")
                .withTransitivity()
                .asFile();

  archive.createFacesPortlet("JsfFormSubmit", "JSF Form Portlet", "form.xhtml")
              .addAsWebResource("form.xhtml", "form.xhtml")
              .addClass(Bean.class)
              .addAsLibraries(libs);

  return archive;
}



 

We need to be able to access the input field and submit button in the browser, so we use Graphene:

@FindByJQuery("[id$=':input']")
private WebElement inputField;
@FindByJQuery("[id$=':submit']")
private WebElement submitButton;



 

We use the JQuery FindBy from Graphene as JSF prefixes the ids we specified on the JSF page with auto generated ones. With the above id selector, we're saying that we know the id ends with that text, but it could start with anything.

 

And of course we need to inject Drone, for controlling the browser, and the URL to the portlet instance:

@Drone
WebDriver browser;

@ArquillianResource
@PortalURL
URL portalURL;



 

Now we get to the most interesting bit, actually using Warp in a test on a JSF portlet!

 

For our test we're going to set some new text into the input field, and then verify that the JSF Update Model Values lifecycle phase correctly sets the new value onto our bean:

@RunAsClient
@Test
public void testSettingValue() {
  browser.navigate().to(portalURL);

  Warp
   .initiate(new Activity() {
     public void perform() {
       inputField.clear();
       inputField.sendKeys("modified");
       submitButton.click();
     }
   })
   .observe(request().index(1))
   .inspect(new Inspection() {
     private static final long serialVersionUID = 1L;

     @ManagedProperty("#{bean}")
     Bean bean;

     @PortletPhase(ACTION) @BeforePhase(Phase.UPDATE_MODEL_VALUES)
     public void testBeanValueBeforeUpdate() {
       assertEquals("original", bean.getText());
     }

     @PortletPhase(ACTION) @AfterPhase(Phase.UPDATE_MODEL_VALUES)
     public void testBeanValueAfterUpdate() {
       assertEquals("modified", bean.getText());
     }
   });
}



 

Wow, that looks complicated! Well it does seem that way when you first look at writing a Warp test, but once you've done it a few times you get the hang of it.

 

Here's an explanation of what we're doing above in the test method:

Line 4 - Navigates to the portlet page in the browser. This isn't done as part of a Warp Activity because we're not Inspecting anything as a result of rendering the portlet, but that is certainly something you could do.

Lines 9-11 - Update the input field to the new value and then submit the form in the browser. This is defined as part of a Warp Activity, meaning that when these browser actions have completed Warp will look for requests that occur as a result of what we did in the browser, to run the Inspection against.

Line 14 - Define a Warp group, as we only want to perform an Inspection against a specific request. NB: The GateIn portlet container will cause two requests to occur as a result of a form submission: one for the ActionRequest and another for the RenderRequest.

Line 14 - This group will observe the first request that Warp encounters, which in this case will be the ActionRequest. NB: The GateIn portlet container will cause two requests to occur as a result of a form submission: one for the ActionRequest and another for the RenderRequest.

Lines 15-16 - Create a new Inspection to be passed to the server for execution. At present it is necessary to manually add the serial version id, but this is planned to be fixed in a future Warp release.

Lines 18-19 - Injects the JSF bean into our Inspection. It's possible to inject Portlet request and response objects, JSF beans, or JSF objects (such as FacesContext) into an Inspection.

Line 21 - This Inspection method will run during the ActionRequest, specified by @PortletPhase(ACTION), and before the JSF Update Model Values lifecycle phase

Line 26 - This inspection method will run during the ActionRequest as well, but after the JSF Update Model Values lifecycle phase

 

For examples of other ways to use Warp with Portlets, you can take a look here and here.

 

With the latest Arquillian Portal Extension it opens up new ways to test JSF portlets, without ever having to deploy them to a portal!