Recently, I built a quick and dirty UI test framework for testing web application UIs which could serve the purpose at hand. The frameworks has 3 layers, specs, pages and page_uis… Before I proceed… if you are already thinking about, how it is different from “Page Object Pattern”, here is what I have to say. IMHO, Page Object Pattern is quite over-rated.
To some of us, “PO Pattern” could just be a name given to the obvious way to do UI Testing, while to some others, it may look like the most important pattern to learn and fully understand to master the art of UI Test Frameworks. But the fact is, Page Object Pattern has been around even before it was explicitly called so. In fact, any one going down the path of automating UI would need to build a UI model to interact with the application and it would be any body’s guess that this UI model will quickly be a set of classes mapping to the UI pages and there you go, you have the Page Object Pattern!. And if you are following the DRY principle, you will find yourself building UI Page compositions made from other reusable UI components there by enhancing the Page Object Pattern. So, when ever I speak about Page Object Pattern, I always emphasize that Page Object Pattern does not need to be learned separately, it is what your UI Test Framework would evolve into if you are applying good development principles. For building good test frameworks, it may be more helpful to read Design Patterns. In this context, it may be worthy of note that there have been articles around the efficacy of Page Object Pattern. One presentation worthy of mention is by Anand Bagmar on the “Perils of Page Object Pattern” discussing the numerous ways in which Page Object Pattern had been misunderstood and had lead to ill-performing test frameworks.
Difficulties with the original pattern
In the UI test frameworks that I built, I had never used the Page Object Pattern to the letter. The original Page Object Pattern talks about returning the resultant pages from the page services so the callee layer has fluent interfaces to work with. This aspect would demand that the Pages have a method each for each state the page could result in. For a simple login functionality, it would result in two page services validLogin and invalidLogin each taking the application to a different state. Very soon, this capturing of application state in the page services becomes unwieldy because in several applications, the resultant state from a page may have been because of the user traversing through an earlier page state. Capturing all possible combinations of flows would bloat up the page class without really adding any value. So, I always chose to stay away from it and just capture the services the page offers without worrying about the state to which the action would drive it to. The trade-off is that the callee layer (either a test or a business-logic layer) should be intelligent about the next state of the application and do the needful. And I am fine with this trade-off.
A mix of concerns?
In the classic Page Object Pattern, the UI is entwined with the Page Services. Sounds reasonable. But, in practice, there is a mix of concerns here. I prefer to have the Page UIs with all the locator information in a separate layer, say the page_ui layer and the pages layer work with the page_ui layer and expose the page services to other parts of the framework. This helps me in separating concerns so that if there is a change to the UI structure I know where to look and if there is a change in the UI flow, I know where to look.
Back to my framework
After a good amount of digression, let me get back to the topic of the post. The framework I built with the above thoughts in mind has three layers. One for the specs / tests, the other for the pages and the third for the page_uis.
The heart of the framework
The core component of the framework is a PageComponent class which lets model the UI element hierarchy as a tree in the page_ui layer. This PageComponent class can be a tree of other PageComponents represented as nested maps (hashes in Ruby) or a list of other PageComponents. A PageComponent knows it’s parent hierarchy, so I leveraged CSS selector hierarchy to locate elements. By this, if I have a Search Form with the class “search-form” and a search input with the class “search-field”, I could create a PageComponent with the locator “.search-form” and another PageComponent “.search-field” and make the first the parent of the second and the framework knows to traverse the hierarchy. The simplicity of the framework is in the way these UI trees could be built by an over-ridden “=” and “” operators which takes care of creating PageComponents and establishing parent-child relationships. For the above example, all I have to do is write the below lines (in Ruby) and the framework knows how create the parent child hierarchy and resolve the same when I am reading self[:search_form][:search_field].
self[:search_form] = “.search-form”
self[:search_form][:search_field] = “.search-field”
(The trade-off, of course, is that I can only use CSS for location, which I was fine with.)
With the page_ui layer taking care of encapsulating the UI model, the pages layer exposes the page services making use of the page_ui layer and the specs mostly interact with the pages for performing UI flows and making assertions on the same.
Another aspect of the framework is the lazy-location of elements. The PageComponent locates an element upon a cue of its method “f” and till that point, only stores the locator information.
I created a sample project using Amazon home page with the framework’s core PageComponent class to demonstrate the points mentioned above. I appreciate any feedback and comments!