Using the HtmlUnit API with JSFUnit

Version 13

    JSFUnit uses HtmlUnit as a headless browser to fill in forms, submit requests, and execute javascript.  For most client tasks, you can use the JSFClientSessionAPI object and let HtmlUnit run "under the covers".  But sometimes you need the full power of HtmlUnit.  JSFClientSessionAPI is fully integrated with HtmlUnit such that you are allowed to use both API's in the same test.

     

    For an introduction the HtmlUnit API, click here.

    For HtmlUnit javadoc, click here.

    Getting a Page object

    To manipulate the web page with HtmlUnit, you will need to get a handle to the Page object.  You do this in JSFUnit with JSFClientSession.getContentPage().  Since most of the time your application is serving up HTML in a browser, the Page obect can usually be safely cast to an HtmlPage:

    JSFSession jsfSession = new JSFSession("/home.jsf");
    JSFClientSession client = jsfSession.getJSFClientSession();
    HtmlPage page = (HtmlPage)client.getContentPage();

    Important! You can use this HtmlPage object as long as the server does not return a new one.  For instance, you can use the same HtmlPage object to fill in forms and click AJAX elements.  But any interaction, such as form submission, that causes a whole new page to be sent to the browser will cause your HtmlPage object to become stale.  All HtmlUnit methods that can potentially cause a new page load will return a new Page object.  So it is best to keep the return value of an HtmlUnit method call.

    HtmlPage page = (HtmlPage)client.getContentPage();
    HtmlButton button = (HtmlButton)page.getHtmlElementById("mybutton");
    page = (HtmlPage)button.click();
    

    Or, you can just call client.getContentPage() at any time to get the up-to-date page.

     

    Getting an HTML Element inside the page

    Besides getContentPage(), the other JSFUnit method used to interact with HtmlUnit is JSFClientSession.getElement().  This method returns an org.w3c.dom.Element object that can be cast to an HtmlUnit object such as an HtmlForm:

    HtmlForm form = (HtmlForm)client.getElement("myform");

    Using XPath

    The usual way to mimick a user in JSFUnit is to use the JSFClientSessionAPI object.  It has three methods that let you fill out forms and click on things.  Those methods are setValue(), type(), and click().  Each of these methods takes a componentID from your JSF markup as a parameter.

     

    But sometimes, the HTML element that you need doesn't have a known ID attribute.  Or, the value of the ID is hard to determine.  In that case, you can use powerful XPath expressions and functions to find the HTML element(s) you are looking for.  There are several good XPath tutorials on the net and it only takes a couple of hours to learn it.  Click here or here for a couple of them.  Also, there is a great tool that helps you build interactive XPath queries with FireFox's FireBug.  It's called FireXPath.

     

    To use XPath in JSFUnit, you will need to use HtmlUnit's DomNode.getByXPath() or DomNode.getFirstByXPath().  Almost every element in HtmlUnit inherits from DomNode, so XPath is usually available.  Even HtmlPage is a DomNode, so you can do an XPath query against the entire page.  In this example, we get all the input tags from the entire page:

    JSFSession jsfSession = new JSFSession("/home.jsf");
    JSFClientSession client = jsfSession.getJSFClientSession();
    HtmlPage page = (HtmlPage)client.getContentPage();
    List<HtmlInput> inputTags = List<HtmlInput>page.getByXPath(".//input");

    Usually, you won't want to qurey against the whole page.  You just want to find one or more elements starting at a certain place in the JSF markup.  You can use both the JSFUnit API and the HtmlUnit API together to limit this search.  This example looks inside a form for the first div tag with name="foo":

    JSFSession jsfSession = new JSFSession("/home.jsf");
    JSFClientSession client = jsfSession.getJSFClientSession();
    HtmlForm myForm = (HtmlForm)client.getElement("myform");
    HtmlDivision myDiv = (HtmlDivision)myForm.getFirstByXPath(".//div[@name='foo']");

    Testing Javascript

    HtmlUnit allows you to test your javascript.  Click here for the HtmlUnit documentation about this topic.  That document and the tips above should get you started.

    Responding to Prompts, Alerts, Attachments, etc.

    HtmlUnit allows you to set listeners so that you can respond to browser events such as prompts and alerts.  Scroll down to the "Watching for 'alerts'" topic on this page for details.  You will see that you need to set your listener on the WebClient object.  To do this in JSFUnit, you will need to do some extra setup for your JSFSession.  See the example below:

    WebClientSpec wcSpec = new WebClientSpec("/home.jsf")
    WebClient webClient = wcSpec.getWebClient();
    webClient.setConfirmHandler(new MyConfirmHandler());
    JSFSession jsfSession = new JSFSession(wcSpec);

     

    Other WebClient settings

    There are lots of useful settings that you might want/need on the HtmlUnit WebClient object.  Using the JSFUnit WebClientSpec, you can get a handle to the WebClient before the JSFSession is started.  This is shown in the example above.  You can also get the WebClient object after the session starts by calling JSFSession.getWebClient().

     

    Click Here for the WebClient javadoc where you can see all of your options.

     

    Working with multiple windows

    If your application opens multiple windows, then you need to be able to move from one window to the other and enter input.  Your virtual "cursor" will stay on the main window until you tell HTMLUnit to set a new current window.

    JSFSession jsfSession = new JSFSession("/myMultiWindowPage.jsf");
    WebClient webClient = jsfSession.getWebClient();
    webClient.setCurrentWindow(webClient.getWebWindowByName("mySecondaryWindow"));

    You can also get a list of all open windows like this:

    JSFSession jsfSession = new JSFSession("/myMultiWindowPage.jsf");
    WebClient webClient = jsfSession.getWebClient();
    List<WebWindow> openWindows = webClient.getWebWindows();

    For more information on HTMLUnit and windows, click here.

    Getting a new URL in the same browser session

    If you want to simulate the user typing a URL into the browser session or need to do a GET request for any other reason, you need to use the WebClient and JSFUnit's WebConversationFactory as in this example:

    JSFSession jsfSession = new JSFSession("/index.jsf");
    WebClient webClient = jsfSession.getWebClient();
    webClient.getPage(WebConversationFactory.getWARURL() + "/myotherpage.jsf");
    

    JSFSession, JSFServerSession, and JSFClientSession will stay in sync with the WebClient and you can continue to use those objects normally.  Also, the HttpSession will be the same because the WebClient will continue to set the JSESSIONID cookie.