In my previous post, I have described how I used groovy to create a simple DSL for defining form layouts.

A short reminder – at the bank I work, we have created a little framework to speed up creation of form-based applications. There is a backing bean, corresponding to the form page with a set of annotations defining how those fields should be displayed and a groovy definition of the form layout (sections, rows, tables etc.).

So that is all cool, but now we come to the subject of this post. User Interface tests. In the java world when you think about UI tests, then the answer is Selenium, so this is what we used.

Page Objects

We wanted to have those tests both readable and maintainable. So we decided to write Page Objects for all the pages we hacked, to at least test the success paths. You might be asking what is a page object? Very easy – it is just a java object that corresponds to the page you are testing, hiding all the ui-test-framework crap.

For example for the http://google.com the PageObject could look like this

public class GooglePage {
   Selenium selenium;

   public GooglePage open() throws Exception {
      selenium.openURL("http://google.com");
      selenium.waitForPageToLoad("30000");

      return this;
   }

   public void typeInSearch(String query) throws Exception {
      selenium.type("css=input[name=q]", query);
   }

   public Iterator<Result> search() throws Exception {
      selenium.click("css=input[name=btnK]");

      // parse the results and return the iterator
   }
}

That approach makes it then very easy and elegant to write tests. Cause you can just do:

public class TestGooglePage {
   @Test
   public void testSoftwareMillRules() {
      // given
      GooglePage googlePage = new GooglePage().open();

      // when
      googlePage.typeInSearch("best software house");
      Iterator<Result> results = googlePage.search();

      // then
      assertThat(results.next().getLink(), "http://softwaremill.com");
   }
}

And that’s it – you can immediately see what is going on, what are we trying to test. The testing code is not polluted with some selenium nonsense with selectors etc., and most importantly if for example search field name changes and we have to update it, we do it in one place not in 300.

Groovy wrapper

OK, enough with the introductions. Where is the Groovy I came for, you ask.

So I thought – we have a bean that backs our page. We have an objective representation of the form, when the layout is parsed etc. Why do we even have to make those PageObjects. We should be able to do some groovy magic.

So the approach I took is as follows. Groovy takes two things – the class of the backing bean, which gives us a nice way to access all the fields plus the parsed form which takes the form layout, reads all the annotations, and creates the representation with information like – is the field read-only, how is it rendered (date, text input, textarea, money etc.) and such.

And then the magic happens – groovy mocks an instance of the backing bean and under the hood adds the wrappings to Selenium.

So first let’s wrap our class

    public PageObjectWrapper(Selenium selenium, String url, FormConfig formConfig) {
        this.selenium = selenium
        this.url = url
        this.formConfig = formConfig
    }

    public <T> T wrap(Class<T> jaoClass) {
        this.jao = mock(jaoClass)

        jao.metaClass.getProperty = {
            String name -> return getValueOfField(name)
        }

        jao.metaClass.setProperty = {
            String name, Object object -> setValueOfField(name, object)
        }

        jao.metaClass.open = {
            selenium.open(url);
			selenium.waitForPageToLoad("30000");
            return jao;
        }

        jao.metaClass.submit = {
            selenium.click("xpath=(//input[@data-jastin='SUBMIT'])")
		    selenium.waitForPageToLoad("30000");
        }

        return jao
    }

What is going on here? I have passed a JAO class (JAO is the backing bean) which then gets mocked (so we get a proper instance of this object) and beautified. If you remember my previous post, the getProperty and setProperty will intercept all property access on the mocked instance and forward the call to methods getValueOfField and setValueOfField. We’ll get there.

Then I am adding some extra methods on the bean – open will open the actual url, submit will submit it (doh!).

You might be asking – so what is this @data-jastin attribute on the input? Well this is something we added in HTML which makes it easier to access the fields. Every time a field(input, output, whatever has a mapping on the backing bean) is generated it either has a constant data-jastin attribute (like here SUBMIT) or the field name from the backing bean.

Ok so let’s take a look at those methods now.

    private getValueOfField(String fieldId) {
        def field = formConfig.getFieldByName(fieldId)

        Object fieldValue;

        if (field.type == Type.DROPDOWN) {
            fieldValue = selenium.getValue("//input[@data-jastin='" + fieldId + "']");
        } else {
            fieldValue = selenium.getSelectedLabel("//select[@data-jastin='" + fieldId + "']");
        }

        fieldValue.metaClass.fieldName = {
            return fieldName
        }

        return fieldValue;
    }

    private setValueOfField(String fieldId, Object value) {
		def field = formConfig.getFieldByName(fieldId)
			
        if (field.type == Type.DROPDOWN) {
            selectInDropdown(fieldId, value)
        } else {
            type(value.toString(), jastinField(fieldId))
        }
    }

I have obviously not put all the field types we have, cause the listing would be much bigger, but it is just to give you an idea. FormConfig hold our objective representation of the form definition, from which we can get the field we want to set/get and read it’s type.

Once we have a type, we can call the appropriate selenium command and ta-dam! That’s it. So how will the usage look like? Let’s pretend we’re testing the form from the previous post.

class UserFormTest extends AbstractSeleniumTest {
    @Test
    def testSubmits() {
        // given
        def page = page()

        // when
        page.name = "Tomek"
        page.lastname = "Szymański"    
        page.shoeSize = "43"

        page.street = "Street"
        page.streetNo = "1"
        page.appartmentNo = "1"
        page.city = "Warszawa"
        page.country = "Poland"

        page.submit()

        // then
        assertThat(selenium.isTextPresent("User saved!")).isTrue();
    }

    def page() {
        // this gets the objective representation
        FormConfig form = GroovyParser.parse("/groovy/UserForm.groovy");
        // and this returns the page
        return new PageObjectWrapper(selenium, "http://localhost:8080/user", form).wrap(UserJAO)
    }
}

And that’s it. Perfectly readable, almost none selenium (just for asserts, and just because, we’re looking for a message on a field. If it was part of the framework, we could’ve wrapped it and add appropriate method on the jao).

Summary

I bet you’d like to see the whole thing in action. I wish I could just put it open-sourced somewhere, but… .

I wanted to give you an idea on what you can do with wrapping your super-stable staticly-typed Java code ( 😉 ) with a dynamic language like Groovy. You can very easily make your (and your co-coders) life much easier, especially when it comes to testing.

Hope you enjoyed. Stay tuned for more.

How you can use groovy to make automatic selenium page objects

Leave a Reply

Your email address will not be published. Required fields are marked *