Locators in Page Objects | Bondar Academy
Course: Playwright UI Testing with TypeScript
Module: Page Objects
Instructor: Artem Bondar
Lesson Summary
This lesson discusses how to organize locators within page objects in Playwright. Two approaches are presented: the recommended method by Playwright and a personal preference. Playwright's Recommended Approach Playwright suggests separating locators from functional methods to enhance reusability and maintainability. Here’s how to implement this: Create fields for all locators, such as FormLayoutsMenuItem , and define them as read-only properties. Assign locator values within the constructor. Use these fields in functional methods instead of defining locators directly within them. This method adheres to the DRY principle (Don't Repeat Yourself), allowing locators to be reused across different methods. Personal Preference The instructor prefers keeping locators within functional methods, arguing that: It maintains simplicity, especially in smaller page objects. It reduces confusion when dealing with numerous locators, preventing duplication and dead locators. Debugging is easier as failures point directly to the line of code where the locator is used. The instructor emphasizes the KISS principle (Keep It Simple, Stupid), advocating for a straightforward approach that enhances code maintainability. In conclusion, while Playwright's method has its merits, the instructor finds that keeping locators within functional methods works better for their projects. The choice ultimately depends on personal or team preferences.
Video Transcript
In this lesson, we will talk about how to organize locators inside of the page objects. So PlayWrite has its own recommended approach how to organize locators. And I have my own approach that I prefer to use. So I will show you two approaches and it's up to you to decide which one you would like to use. So let's get into it. So PlayWrite recommends separate locators from the functional methods. So this is our functional method and the locator located inside of this method to perform the click to the form layouts, for example. But PlayWrite recommends to keep all locators separately from the methods itself. How to do that? So let me show you. First of all, we need to create a field for all the locators that we want to remove from the methods. This is GetByTextFormLayouts, DatePickers, SmartTable, Toaster, and TooltipPage. So let's create a new field for them. ReadOnly and we need to provide a meaningful name for each of those. For example, first one will be FormLayoutsMenuItem. And the type of this field is Locator. And we click Enter and Locator object is imported from the PlayWrite test library. And just following the same example, we need to create the rest of the fields for each of the locator that we want to remove from the functional method. Okay, this part is done. And now we need to assign the value of our locator to all these fields that we just created. And we, of course, doing this inside of the constructor. And I called this FormLayoutsMenuItem equals and take the locator from the method. Put it here and then replace this field name inside of the method like this. That's it. So we remove the locator from the method and assign to its own property name. And then we call this property name or field inside of the method and performing the click. So following the same approach, we need to make a refactoring for the rest of the methods inside of our page object. All right, we've done this. And now let's look at our final result. So all locators right now together inside of the constructor, the values of the locator assigned to the fields, and we call those fields inside of the functional methods. So let's run this test real quick to make sure that everything is working how it was before. Running this test. Yes, and test is working exactly how it was before. So this is the way how Playwright recommends organizing page objects. And the main argument behind this approach, why do we need to use locators independently from the functional methods? Because you can reuse the instance that points to the locator into different methods inside of the page object. So you kind of do not duplicating your locators and your locator becomes unique. Once you define this locator, you just reuse this field in other methods and you avoid duplication. Remember this dry principle, don't repeat yourself. So this approach follows this dry principle. But in my opinion, this is not very good approach because it kind of violating the second suggested principle, which is keep it stupid simple. And let me explain why. So this is a pretty simple page object with just five small methods related to this page. But in the real life projects, your typical page object may have 15, 20 different methods. And each of those methods may have several locators. So over the time, what your page object would look like is something like this. So let me do this. I will just copy paste this multiple times. So your page object may look like this, a whole bunch of fields defined in the class and the same you will have inside of your constructor like this. And now when you look at this kind of view, I think you may agree with me that it's kind of difficult to read the names of each of the method. And when you have like 30 or 40 locators, it's very easy to get lost inside of them. And just to understand, okay, do I have the locator that I need? And what also very often happen, and I saw it on the other projects, that if few engineers working on the same project, they become creating a duplication of the locators, but using a different name. For example, they do something like this. This is the tooltip menu item, but other engineer can call it tooltip menu selector, for example. And now we can create a second field, read only, tooltip menu selector, locator. And now our list of the locators have two exactly the same locators, but with a different name. And also sometimes you may have a situation of the dead locators that are just existing in the list, but never used. So that's why technically I don't like this approach of keeping the locators like that. Also, what to do with locators like this? Look at this. So we have a locator here, getByTitle, where we pass the parameter inside of the locator instead of actual value of the locator. You cannot remove this type of the locator from method inside of the constructor, because you will have a complaint on this property that does not exist. So you end up having locators in two different places. Some of the locators will be inside of your constructors, but some of them which you can't remove will be still inside of your methods and it will be mixed again, right? So looking at all of those arguments, I personally prefer not following this approach and not using the locators inside of the constructor and separating them from the actual functional method. And if you're worrying about duplications of your locators in the few different methods that you need to copy paste the locators, just trust me. If your page object methods are functional enough covering good scope, most likely you will have two maximum three duplications across of all your methods. But when you have locators inside of your functional methods, it's much easier to debug and it's much easier to maintain the code. Because if the test is failing, it's telling you exactly the line of code where it fails. You just open this line of code and seeing the locator that was called and it's much easier to debug. So here I would prefer using keep it stupid simple principle and do not over complicate the code and keep your locators inside of the methods related to the page object. Again, this is my personal opinion, guys. It's up to you how to build the page objects. From my experience, I always keep locators inside of the functional methods of page objects and it always worked just fine for me and it always was easy to debug, easy to fix and maintain this type of code. So I'm following the keep it stupid simple principle. So that's it, guys. Let me revert all the changes that I did for this lesson because I will continue following this class with just the standard approach where our constructor have just a page initialized and our locators will be inside of the methods. All right, thank you guys and see you in the next lesson.