Thursday, 20 February 2014

Widget testing with Arquillian Graphene

Introduction

Integration testing is important but also difficult.  With frameworks like WebDriver we can connect to any HTML element in the browser.  This is a huge step forward.  We are able to inspect what is available on the screen, in the browser.
But coding in it is error prone. Any small change in the page layout and your test will fail because the element isn’t found anymore.
Another challenge is to keep your test code object oriented and readable.  Since everything is a WebElement in your code, you must resort to encapsulation and page fragments to not end up with large test methods which are unreadable.

Testing PrimeUI

That was also the path I took when I started testing the AngularPrime widgets, now more then a year ago.
I created widgets wrappers, like PuiInput, PuiCheckbox and so on that knew the internal DOM structure of the widget in the browser. They all have high level methods like click() and isChecked() that shield the internal stuff away from the developer.
But I never came to the point I was satisfied with the code.  That is why the testing part of AngularPrime was never committed to GitHub. It had various helpers and things really got messy when I added more and more widgets. Carrying around the WebDriver instance to have connection to the browser was the ugliest thing.

And then I don’t mention the need for a custom testRunner so that I could run the same tests on the different Browsers.

Arquillian Graphene

Some time ago, I came in contact with the Arquillian Graphene. It is a framework on top of WebDriver that uses Arquillian and other subprojects as Arquillian Drone to have better integration testing capabilities. But in the beginning it was very confusing, because you have so many parts in  the equation. And setting the whole thing up, with all his different maven artifacts, failed more then once.

But about a month ago, I decided to do it the hard way and spend more time in investigating the option to write all integration tests using Arquillian Graphene. I found out that using the client mode testing, where Arquillian is not deploying any WAR artifact to a server, was the easiest.  And in my case also the best solution as almost all my sources are HTML and JavaScript.

And with the Page Fragment feature, it turned out that I could write beautiful structured tests where each widget is encapsulated by a page fragment. So now I’m in the process of rewriting my 100+ integration tests using Arquillian Graphene. But it goes amazingly fast.

Page Fragment

With a Graphene Page Fragment you can encapsulate a part of the page, but also a single widget.  And it turns into a reusable component. Ideal for testing the widgets of AngularPrime.
So this is the class for an easy widget like pui-input (partial code)

public class PuiInput {
    protected static final String PUI_DISABLED = "ui-state-disabled";

    @Root
    protected WebElement root;

    protected boolean containsClassName(WebElement element, String className) {
        return element.getAttribute("class").contains(className);
    }

    public void click() {
      root.click();
    }

    public boolean isDisabled() {
        return containsClassName(root, PUI_DISABLED);
    }

//...
}


So it is a simple POJO class where the encapsulating HTML element is injected in the property annotated with @Root.

This Page fragment can be used in a Page object (not needed for my tests but very recommended in the testing of real applications) or directly in a test class as follows.

@RunWith(Arquillian.class)
public class InputTest {

    @Drone
    private WebDriver driver;

    @FindBy(id = "default")
    private PuiInput puiInputDefault;

    @Test
    @RunAsClient
    public void testDefault() {
        driver.get("http://localhost ...");

        assertFalse(puiInputDefault.isDisabled());
    }

}


The special test runner Arquillian does all the magic. @RunAsClient indicates that we don’t want to deploy anything on the server and that the tests runs as a client, not on the server. The @Drone annotated field gets the object that connect to our browser. By default this is the PhantomJS (headless browser designed for testing) but we can also choose to use Chrome, Firefox or any other supported browser.

The puiInputDefault property gets populated by our page fragment, and the @Root annotated field, within our Page Fragment, will get a proxy to the HTML element with id default. Indeed a proxy as when our test class is instantiated and injected with all those objects, the browser isn't available yet and thus a real link to the HTML element is not yet possible. The proxy is resolved at the time we first access the real WebElement, in our little test case, this is at the time we call the isDisabled() method.

More advanced page fragments.

A widget like pui-checkbox has a more complex DOM structure but this is no problem for Arquillian Graphenes Page fragments.  This image shows the structure of the widget.

pui-checkbox-dom

In the case we make a Page Fragment, our root will point to the, now hidden, input field. And the most interesting parts are the divs with class pui-checkbox-box and pui-checkbox-icon.

But with a page fragment, we can reference also other WebElements then the root.  This is how the PuiCheckbox class could look like.

public class PuiCheckBox {

    @Root
    protected WebElement root;

    @FindBy(xpath = "../../div[contains(@class, 'pui-chkbox-box')]")
    private WebElement box;

    @FindBy(xpath = "../../div[2]/span[contains(@class, 'pui-chkbox-icon')]")
    private WebElement icon;

    protected boolean containsClassName(WebElement element, String className) {
        return element.getAttribute("class").contains(className);
    }

    public void click() {
       box.click();
    }

    public boolean isChecked() {
       return containsClassName(icon, "ui-icon-check");
    }
}


The difference with the PuiInput code, is that we now have additional properties annotated with @FindBy.  They refer to other WebElements but relative to the root.  According to the DOM structure of pui-checkbox widget, the box links to the visible area of the widget where you should click on. By using the icon element, we can determine if the widget appears checked or not.

Need for WebDriver ?

In the case you do need to access the WebDriver instance to perform any action on the browser, like for instance interacting with an alert, you can annotate a property with @Drone here also.  Then the WebDriver instance is also injected in to your Page Fragment.

Conclusion

With Arquillian Graphene, we are able to create object oriented tests where we can encapsulate some screen parts of the browsers. Due to the nice dependency injection, your code becomes clean and readable.

Next time, I’ll discuss some more advanced widgets like the radio button group and the solution I came up with for handling such cases.