Sunday, July 6, 2014

Custom locators

In this article we'll talk about custom locators implementation. Well, I guess some of you would probably ask - why do we need to reinvent the wheel, if it already exists in a form of FindBy annotation + WebElement bunch or just By class?

Let's start with some basics. I prefer using By class, as it's more flexible. But readability and bulkiness of the following construction:


- cries 'refactor me', unlike FindBy + WebElement bunch, which is pretty laconic and straightforward. So the first reason of such customization was aspiration of decoration to increase readability and code understanding.

Let's assume we've decorated our locator. What other advantages could this technique give us? Well, now we can solve a problem of creating locators on fly (dynamic locators). Actually, there's no problem, but for me it's quite strange to create locators dynamically within PageObjects' methods. Such 'no name' elements are dropped from a real page model. Yes, someone can say that in such case we need to use PageElement pattern, but it doesn't make sense, when we use only common page controls.

Saving space. Let's assume that we have lots of identical elements without unique attributes, which require quick access for any kind of interaction, e.g. getText or click. We can create dozen of locators, which differ only by index or text or just use 'no name' dynamic locator within PageObject's method. In first case our PageObject will swell and become hard to read. And of course it's a moveton to use indexes or text inside locators, as it wouldn't be possible to support them. Second case's disadvantage was described earlier.

Passing custom parameters. Custom locators gives us an opportunity to pass custom parameters to any decorated element. As you may know, we can't do this via pure Selenium annotations or By class.

Now let's look at implementation. We would annotate all the elements with HTML annotation:


Let's decorate By class:


This wrapper implements updateElement method, which allows us to create locators on fly. We'll see how it looks like in further PageObject's implementation.

Now we need to provide some way of custom locators' initialization. We can achieve this via java reflection within abstract page class:


And we shouldn't care which page has triggered elements' initialization. This mechanism supports chain inheritance, when we need to move through all child pages to their parent one.

Sample PageObject may look like the following:


As you can see, this example contains both static and dynamic locators, which are marked with special ? symbol within path. This symbol will be replaced with missing member (which is needed for elements' unique identification on a page) in runtime.

You can pull a full example from GitHub.

No comments:

Post a Comment