Sunday, April 19, 2015

PageObjects done right in Java 8

I'm currently working on a project and there was a lot of buzz around protractor.  My team was excited about using it as we're mostly front end devs with a passion for javascript.  Plus we're using angular, so protractor felt like the most natural fit.

On my team we're really into automating things, and we have Jenkins to handle most things for us.  Luckily we have automated dev deploments every 30 minutes, so plugging in some selenium automation post deployment was worth it's weight in gold to us!

Issues with Protractor and moving back to Java

Issues started to arise with protractor once we realized we were limited to using it with PhantomJS in our AWS Jenkins Slave environments.  Currently, protractor advised against using PhantomJS due to known issues as stated in their documentation.  This was a non-starter.

Being that I know Java, and that our backend is written in Java, I thought I would turn to some of the new Java 8 APIs to see what they had to offer.  I realize I'll lose most front-end devs with what follows, but I feel the first class support of Java in the Selenium world, and the maintainability of the technique I'll outline shortly make it the best option available given our circumstances.

A bit about history, theory, PageObjects, and the like...

We've had direction for some time now from to organize our tests into PageObjects.  These PageObjects take on the form of classes in Java.  The traditional way PageObjects were written required some strange solutions for widgets that get used on many different pages.  You basically had the following options pre Java 8:
  1. Assign widgets to fields inside your Page Objects.
  2. Static methods on Widget classes.
Neither of these 2 solutions really scale on a large site where it's normal to have 20 or so widgets on a page.  Creating lots of Widget instances leads to memory and performance issues.  Using an injector system works, but it's way outside the comfort zone of most QA personnel and I want to keep things simple for the front-end devs who need to come up to speed quickly using Java.  Static methods on Widget classes do not account for driver management, and running tests in parallel.

Viewing Widgets as Mixins

After thinking about it for some time, I realized that the previous 2 options never felt right because they fail to model websites correctly.  In the context of a website, a widget never has any context or usability until you've navigated to a page that contains them.  In principle, instantiating a Widget class doesn't make sense.  Instantiating a page that you navigate to does.  Once I started thinking about this, I realized that Widgets are perfect as mixins, and that Java 8's addition of default methods solves this issue nicely!

Creating Mixins in Java 8

In Java 8 it's really simple. Using the virtual field pattern, our widgets can extend interfaces for retrieving instances of drivers and pages.  Here's a sample Widget written as a mixin using default methods:

Why is that so amazing? Because we can now add it to any of our PageObjects without worrying about instantiating anything! Here's how we add our Widget to a PageObject:

Since our HomePage object extends Page, better show you it's implementation as well:

Here's how we use it in our tests:

We can now add SearchWidget to any PageObject by simply implementing it in our PageObject's class.  What's more, we can create more Widgets and adding them to our Pages is simply adding another interface that the page implements.  This models the real world pretty closely, and it's very easy to maintain.  Our tests are also easier to read.  We could invite people with low technical skills to write test cases, and let developers or automation engineers implement them, but that's for another day :P

No comments:

Post a Comment