Selenium vs. ZK

30. August, 2013

Testing ZK applications using Selenium can be a drag. Selenium offers a lot of tool to test traditional request-response cycle applications. But it relies heavily on stable element IDs and submitting whole forms.

ZK, on the other hand, sends just a skeleton page to the browser and from then, builds everything with JavaScript. Instead of posting a whole form, it submits individual field values (to trigger validation). And it assigns random IDs to reach element.

At first glance, the two don’t seem to be a perfect match. But there are a couple of simple tools that will make your life much more simple.

Getting Stable IDs for Tests

One solution here is to write an ID generator which always returns the same IDs for each element. But this is tedious, error-prone and sometimes impossible.

A better solution is to attach a custom attribute to some elements which doesn’t change. A beacon. If you have a central content area, then being able to find that will make your life much more simple because whatever else you might seek – it must be a child of the main content.

To do that, add a XML namespace:

    <zk xmlns:cl="client" xmlns:ca="client/attribute">

to the top of your ZUL file, you can now use a new attribute ca:data-test-id="xxx" to set a fixed attribute on an element. In Selenium code, you can now locate this element using this code:

    By.xpath( "//div[@data-test-id = 'xxx']" )

I suggest to use this sparingly in order not to bloat your DOM. But a few of them for fast moving targets will make your tests much more stable.

Debugging the DOM

Sometimes, your life would be much more simple if you could see what the DOM was when your test failed. Here is a simple trick to get a HTML fragment from the browser:

    protected JavascriptLibrary javascript = new JavascriptLibrary();

    public String dump( WebElement element ) {
        return (String) javascript.executeScript( driver,
            "return arguments[0].innerHTML", element );
    }

You can now use JTidy and JDOM to convert the fragment first into W3C XML nodes and then into JDOM elements:

    public org.w3c.dom.Document asDom( WebElement parent ) {
        String html = dump( parent );
        Tidy tidy = new Tidy();
        tidy.setShowWarnings( false );
        tidy.setErrout( new PrintWriter( new NullOutputStream() ) );
        org.w3c.dom.Document dom = tidy.parseDOM(
           new StringReader( html ), null );
        return dom;
    }

    public static org.jdom2.Document asJDOM( org.w3c.dom.Document content ) {

        // JDOM chokes on: org.jdom2.IllegalNameException: The name "html PUBLIC "-//W3C//DTD HTML 4.01//EN"" is not legal for JDOM/XML DocTypes: XML names cannot contain the character " ".
        Node docType = content.getDoctype();
        if( null != docType ) {
            content.removeChild( docType );
        }

        DOMBuilder builder = new DOMBuilder();
        org.jdom2.Document jdomDoc = builder.build( content );

        return jdomDoc;
    }

Another great use case for this is a default exception handler for tests which saves a screenshot and a copy of the DOM at the time a test fails. No more guessing why something didn’t happen.


Debugging proxy.pac Files

29. August, 2013

I just wrote a small utility to debug proxy.pac files using the excellent pacparser library.

You can find it here (gist).


Why God Can’t Exist

7. August, 2013

A little thought experiment. Nothing in an experiment is true but it can help you understand.

God is said to be almighty. If She is, then She should be able to create the perfect world, a place where nothing bad or evil ever happens.

But such a place would be incredibly boring.

Boredom is bad.

Hence God can’t be almighty. Without this attribute, God is incomplete.

Or maybe our universe is perfect – we’re just unable to see it because we whine all the time about everything bad and evil that happens instead of enjoying the good things.

PS: If God exists, She must be female. Or did you ever see something created by a male that kept working for more than 5000 years?