Nowadays there are a lot of talks about page objects and the screenplay pattern. Let me also express some thoughts regarding this. In this article I will apply practical knowledge on the sample mobile project using Calabash framework.
If you don’t use page objects in your project, do it! Seriously, just drop other plans and implement it right away. Page objects are very easy to be implemented and they drastically improve project maintainability and scalability.
But how Page Objects can help my project?
Let’s try to bring out some points:
- You identify all the element paths for a screen in separate files and classes.
- You implement page objects for this screen in a certain place of your code.
- You can call the created page object from any place of the code.
- You don’t care about all the hardcoded elements in your code when something changes. You just change the element in one single place.
- You don’t duplicate the code.
- When you call the page object from your code, you can actually understand which object it is, because you don’t call an id or a class of the element itself. Instead you call something like:
WelcomeScreen.loginButton, in this way it is more readable.
Isn’t it enough for at least giving it a try?
Screenplay Pattern vs Page Objects
From my point of view there is nothing to compare. The idea of Screenplay pattern is very interesting, but it can’t just replace page objects, as it also uses them. The only difference is that Screenplay pattern will give you less possibilities “to shoot yourself in the leg”, it will not allow you to create page objects with logic and user interactions inside. Instead, the pattern uses lightweight page objects, so it is more convenient and maintainable.
The page object’s initial idea was having paths to the element and possible interactions (e.g. click, swipe and etc.) in the same place. That is something what is hard to maintain, especially when you are working in agile world and everything changes on a daily basis. Just imagine that one of your screens will be re-factored and you will have to re-write both: all the paths to the elements and interactions.
Lightweight page objects contain only paths to elements. Afterwards, your scenario will take care about interactions which it wants to perform. That makes the creation of page objects easier and also improves maintainability, because the page object in this case is just one line of code.
There is a good news: lightweight page objects are not something that one can’t use in the project without the screenplay pattern. In Internet you can find a lot of automation examples which use lightweight page objects, and also the sample project for this article is based on them.
How to create Page Objects
- Create a class with the name of your screen. Here you will place all the page objects that exist on this particular screen. Example here.
- Include libraries or inherit classes that allow you to use page objects. In case with Calabash and Ruby you will need to include a class that inherits from IBase for iOS or ABase for Android.
- Identify trait for the screen. In the trait you define a unique element that you can’t find on any other screen. By using it, you can make sure that you are exactly on the screen that you want to access and not accessing something that is on another screen. I’m using it here. The trait checking takes place in
- Create methods within the class with the name of your element.
- Add path to the created method. The syntax will be different for every tool, but in general paths contain information about your element. That can be Class/ID/Text/Label or hierarchy dependencies between elements, as well as their combinations (e.g.
“UIButton sibling UILabel text:’My Green Label’”)
Parameters for paths
There are a lot of ways to find paths to the elements. But the only thing you should care about is the way of finding the unique path. In this way you will reduce amount of random failures and false positives. Of course it is hard to foresee everything, but you can try to do your best and describe the elements as precise as possible.
Probably the most precise method, that every tool will provide, is element ID. Later in this article I will add some words on using IDs in page objects, but now ID looks like a very good way of finding unique path to the element. Nevertheless you will still have situations when some elements have the same ID. For example, you can have a lot of ‘Like’ buttons on the same page, and all of them will have the same class and ID. That’s where hierarchies are coming into play. For such elements you will have to find a unique path. Most likely it will be the parent cell in which the button is placed (e.g.
“UICell text:’Story I want to Like’ child UIButton id:’like’”).
The other possibility to find an element is the view hierarchy. Hierarchies are used in all tools. In Web you’ll have possibilities to use
css, in Calabash the format is a bit different but the idea is the same. Here you have 4 possibilities to build your hierarchy: parent, child, descendant and sibling. One example of using hierarchy you can find here. There is no limit in number of hierarchies that you can use in the single element. Sometimes you can use 2, 3 or even 4 hierarchy dependencies in order to find a unique path.
When I define elements I try to define at least two properties in the path, which allows my page objects to be more precise. Available properties are: text, id, marked and class. I definitely recommend to use the class every time. In addition to the class you can append marked, text or id. So, when as a result you have an element like:
“UICollectionView marked:’Addresses’”, you can be more or less sure, that this will not mess up with another element in your view.
When you have elements that you can find all over the application (e.g. ‘back’ button), you can inherit these elements from other screens. This will allow you to shorten your page objects by eliminating the duplicated code. Sounds really good. But in fact you will make your code more complicated, what will produce a lot of confusions in the future. Imagine that you open a class with your page objects trying to find a back button, but this button is described in one of another 100 classes. So, on some point after several minutes of unsuccessful searching you are creating a new back button and override the existing one. That will make a lot of your tests red, and you will have to spend some time debugging it. So, codewise, the idea of inheriting is great, but in fact this will make your life harder, as page objects have a bit different meaning than a regular code.
Using IDs in page objects
ID is something unique. By using it, you are taking out the complexity of utilizing a lot of hierarchies in your page objects. But there are some downsides. Firstly, by checking IDs you can’t be 100% sure that element you are accessing is correct. I prefer to use element text when it is possible. Doing this you additionally check that the text is correct and also the probability that you are checking something else is smaller. Secondly, if your project is already big and you haven’t had any IDs before, it can be really tough to implement it for the whole project. Most likely your PO will prioritize this issue very low, as it doesn’t bring any visible value to the product.
Note: Mostly this is an iOS problem, as the system doesn’t require having IDs everywhere. On Android you have to add IDs if the element of its kind is not unique on the screen.
No logic in your Page Objects
Do not allow having something else than the path to the element in your page objects. You will find a lot of situations where it is really easy to add some conditions in your page objects rather than improve your tests. But, believe me, you will get a lot of problems with that in the future, as something can be changed in the element and it will be really hard to debug.
This is all I wanted to write about page objects in this article. Implement it in your projects if you haven’t done it yet, or re-factor existing implementations if you don’t use it in the optimal way. You can check how it works on the practical example. If you are not familiar with this sample project, you can find description in my previous article.