 Good morning, everyone. I thought it's best to get started sooner. We have everyone sitting and willing the thumbs or browsing the web. Why not get started? Is it OK? You guys were really getting bored, OK? So before we get started with the core content, I want to get a feel of what that spread is in the audience in terms of technical capabilities or rather preference, rather. So how many of us actually use Selenium Automation or variants of it? Not surprising, that's great. C-sharp, a few. Java, majority. JavaScript, OK. Python, Ruby. Anything else I've missed out? Groovy. Groovy. Tickle. OK, Derek uses me. OK, so what I'm going to be doing today is I want to share my thoughts around why I call these petals of page-optic pattern. It's a pain, really is a pain. Why? Why is it a pain? How many of us work on or rather have test suites which are less than 100 in number, 100 tests in number? Anyone? That's almost. Less than 500, or around 500. More than 500. I don't even want to go beyond 500. Is it easy to maintain that test suite? How often does the product change every day? How often do you spend time in maintaining the results? Do you get time to test? OK, so I'm Anand Bhagmar. I've worked with ThoughtWorks since almost five years now. I've been in the testing field for about 17, 18 years, done various different forms of testing. And I really love doing that. One of the biggest things that frustrates me is having to do the same thing over and over again and not finding better ways of optimizing myself, learning new things, and trying to do things more efficiently. That's enough about me. What is it that you guys expect this session? One thing I got to know, to get selected in a conference, you have to make the title really snazzy. You have to make the abstract, the description really so appealing. It's almost like selling something. You have to get people to come and listen to you. But now that you guys are here, what is it that you really expect? Because if not, we have to use what Nareesh called the law of two feats. Maybe some people will get seats. And by the way, there are some seats over here if you want to say. Second row. So what is it that you expect from the session? Best practices of automation. Work around for specific issues. We will not be talking about work arounds, but some best practices we will touch upon. Lessons learned on the page object model. Yes, we will be covering that in certain ways. Are we doing the same as other companies are doing? So learning from others, mistakes, all things that are working for them. And hopefully to avoid the same mistakes. Ways not to use the page object pattern. Really good point. And we will not be talking about it. OK. So yes, we will be covering a lot of these things in certain ways. But the most important message I want to bring out is think out of the box. Don't just look at best practices and start using it because they are best practices. They are best practices in a certain context. Would I use the patterns that Derek mentioned earlier in each and everything that I do? It is not going to work. He mentioned about some gotchas. Those gotchas are based on specific context. Same is for page objects also. Or same is for any best practice for that matter. Open your mind. Wear intelligent people. Look at what is going to work in the context and choose a ton of practices that you can select from. Choose the ones that is going to work in your context. So what is test automation? Let's start with the basics. And I can really drag this on. I know it's lunch after this session. So if I don't get answers, I'm going to keep dragging on. What is test automation? Why do we do test automation? Saving time. Reproduce is regression. OK. Cost-cutting. Bad reason. We need to talk separately. A reduced manual labor. Not really. OK. QA? Do nothing. That's why you do test automation. Have you come alone from your company? Just checking. Doing a safety check for your own. Sorry, USA. Coverage. OK. Sorry? Improve accuracy. Yes, in certain ways. Accurate test is as good as the type of test you write, though. Yeah. Faster feedback. Protection from manual errors. A lot of correct points. And I'm sure we'll have at least 100 more such different benefits or negative things that come out of it. Essentially, automation is a safety net. You cannot execute your 500 plus. I'm sure if you really go into it, I think the earlier talk was about the current parallel talk is about how Salesforce has really had more than 30,000 test cases and how they really make efficient use out of that. We are speaking just 500. That itself is scary for me. But it's a safety net. You cannot rely on manual regression for any change that happens in your product, especially if that change is happening on a daily basis. And that's where automation helps. Now, I'm not going to talk about benefits of automation or how to do automation right or wrong. That's a completely different topic. And I could speak hours about it. I'm sure others could also contribute to hours and hours of content on that. But let's look at automation as an objective. And how can you build a good automation framework around this? Let's take a simple case study, which hopefully everyone can relate to. Amazon.com. Anyone not seen Amazon.com page? Everyone knows what we are talking about over here, right? Just setting that baseline. So what are the different aspects that you would test from Amazon.com page? This is just the homepage snippet which I got there before yesterday. What are the different areas you would want to test? Category, log in, log out, search, products, images, various payments, yes? We are not even talking about most of those things over here if you see the payments and all. But we do have some critical functionality without which Amazon does not make sense. One is search. If I cannot search for product, everything else is useless, right? That is the most important functionality. Then there's also a menu bar or a header bar of sorts which allows common navigation across various different things that they allow you to do. Then there is ads. Ads is a big money-making business. You have to allow that. And if you don't test that properly, you're losing big money. There is, of course, featured products. They are featured here for a specific reason. They want to generate more traffic, more revenue, more sales out of it, whatever the business model might be. There is this gallery of sorts or carousel, recently brows, or recently items you have viewed kind of carousel, right? Again, important why? You want to get the affinity of the user to buy more items from your site. And then there is, of course, a sign-in. And in sign-in account-related, there is a bunch of different things that can be done over here. And there are, again, different forms of ads. This is the basic things that I came up with. Within half a minute, what are the different aspects that are required to be tested? Now, if someone has to start testing Amazon.com today, after the fact that the product is built, what are the different ways you could start automating this? So there are various different things that you would want to test, right? The simplest thing, again, comes out to work. If I come to a home page, what is the thing that I want to test? Do I need to log into any of those? Probably not. So if we take that specific example, I'll start it off as only QA or the only person doing automation on Amazon.com. How do I start building up the page? So if you look at it in some form of diagrammatic view of sorts, I have my test intent, the domain functionality, to do the action, that verification, what I want to validate. And how do I interact with the product after the test? That is my intent, that's what I want to automate. And to automate that, I have some resources to say, OK, where am I? What credentials am I going to use, for example? That might be a separate resource file. And then you would put a web driver or any other table to say, yes, this is how you should interact with the product to get the function written. There might be some helpers and utilities that you have to make streams or to do logging or whatever. Now, this is how typically you would start building up the platform from scratch. I have an intent. Let's start doing that. So let's look at some code sample of a potential way what this test can look like. Now, this is a JavaScript test. And the code itself is not as important as the concept is what I'm talking about. I'm purposely using code samples from various different languages because it's not the language that is important over here. It is a concept. So in this case, what we have is this is a Ruby code. Sorry, it's a JavaScript code. And we have said on the top, what is it that I want to use? What library is that I want to use? This is how I would describe my JavaScript test. What is the intent? And I start automating that intent. In this case, I'm instantiating my web driver over here, setting its driver properties over here. I'm browsing to a specific URL over here. I'm finding elements. Everything is happening within that test itself. Problem solved, a good way to get started to ensure that you're using the right libraries you're able to interact with the product and you're able to get some validation done. We see there's an assertion also, right? So all assertions, validations, set up, everything is happening in one place. Will this work? Sorry, one at a time. So what's the problem in that? Because it is changeable, sir? Tightly coupled. Tightly coupled. Explain. So better to keep some extra side of property in that. If you need to change, let's go to that property by the way you can change that. So change needs to happen, okay? Wherever you put it, whether it's in the code or in external resources, external bytes. It's logical. Exactly. The volume of effort required to make that change. In the first test, that is fine. I want to prove that this works. I want to prove web driver works for testing the specific functionality. But if I have to start building from, I want to go from one to 10 to 100 to 500 to 1,000 tests to automate, will this work? Of course not. Because everything is just put in together, okay? So this is where we have to start making order out of this chaos that happens, okay? I really love this statement. It was, I think in the Lost Symbol book, Dan Brown, a really good one. But makes sense. Because this is really a chaotic environment. If you don't correct yourself at this point in time itself, your framework is never going to work out. You will end up doing what you are doing right now in terms of just maintaining your scripts and not testing the product. So what can be done with it? And this is where dictionary helps to say, what is a pattern? Pattern is a repeated decorative design. Something that you would take as a template, apply it in various contexts, and make it easy to use, make it easy to reuse. And that's where a page object pattern really comes in. So what is a page object pattern? I've tried to simplify it in very small sentences of sort. Because how many of us have used page object pattern or know about page object pattern? Almost everyone, right? So I'm going to skip through this part much more quickly. So why do we need page object pattern? We want to model pages in code in order to interact better with it. We also want to simulate user actions. If a user says login or search for specific item, a specific product, how exactly is he or she going to do it? I want to simulate that in an automated script. It is more important because once my product really grows, if a locator changes, I don't want to go in 10 different places or 100 different tests to update that locator. I would rather go to one place. Now where that one place is? Is it a separate file in the framework, a separate Excel file as someone was saying? Any location does not matter. The change has to be a one place change to allow you to evolve your test framework and make sure it is aligned with the product as quickly as possible, okay? It reduces code duplication. I want to search, now search as a header bar is available on all pages in Amazon.com, wherever you go. Why? Because that is their main business value, right? I don't want to repeat this search functionality in each and every page that I simulate. I want to make sure it is reusable and again if something changes, it's a one place change. And of course snippets of page. Anyone knows what it means? Components, explains please. So as you explained in the first slide, right? So there are different components that you identify. So each one of them would be called a system. The components which can be embedded within a page. Exactly. Anyone disagrees? In simplest terms, it seems to make sense, right? Home page has got so many different functionalities I cannot possibly put all that functionality in one page, especially if something is going to be reused in other places also. So you have to start breaking it off in logical chunks. Now components is one thing, also what Derek mentioned earlier, right? Just searching for a list of items. You search for an iPhone, for example. You'd get various different options in that iPhone. Now, how do you really interact with that? You need to start taking it down further. That is what is going to make your test more manageable. So snippets is very important. So what this really means is we've got all these different components and you're going to start logically grouping them in various forms of hierarchy of sorts because a search in itself is one functionality that is part of a menu bar, but menu bar also has user details. It has shopping cart. It has got wish list. It has got ads, various different components which might be also featured, not as part of the menu bar also at some times. It can be part of some other pages. So you need to start building up these pages in modular chunks to represent what it really means. That way, if this ad goes out of the menu bar and it comes next to this featured items, I just have to change which is the parent container of sorts, a parent page and everything will continue working. So what does it mean from automation framework perspective? How does architecture really change? You start separating out the intent, domain functionality, actions, verifications from your pages. Everything else remains the same. And then as you know more about the product, you might start thinking about, oh, I can use web driver to interact with the browser directly, but there might be some web services which is doing the search functionality. Let me test the web services also in a different way because the intent is still the same. It's just how I'm interacting with that product is different. You want to do various different types of testing, right? So your framework starts evolving in that fashion. Now, this is what has happened next. Let's look at some code sample, what this might look like. So this is Ruby code. We are now in Ruby LAN. I've got a home page. And by the way, these are not examples related with Amazon.com testing. These are taken from various different experiences and examples that I found from the web, just as examples. This page class, it is really massive. You have exactly what Derek was talking about. Getters, setters of sort, how to interact with elements, get those actions implemented. But if you really look at it, this home page is almost 1000 lines long. Good, bad, ugly? Ugly? Okay. Depends. Who said it? Why would it matter? Okay. If we have immediate service, there's no other option that we have to go through. Okay, so I like the answer, but I don't agree necessarily with it. It depends on the complexity of the application, it's immediately required. From a maintainability perspective, this is where you start questioning it. Does it do the work? Absolutely. It works beautifully well. It's very well written. The only thing is it's not organized well enough to be able to search for things much more quickly, make changes much more quickly. You have to know exactly where to find or what method to find. In that 1000 lines, you have to navigate and find that. Especially as a team grows, everyone will have different styles of writing and problems will start arising. Okay, so that's one example. The other example is, even though you may have your page objects, exactly for the reasons that we spoke about, that teams as they grow, everyone has different styles of writing. In some cases, you might have pretty well written intents or pages which is doing specific things. Okay, you interact with the pages in correct way. But then again, you are saying find elements over here. What is really happening with your abstraction layers? What is happening with your framework? That intent is getting really convoluted. So I have patient name probably inside patient chat page also, but I'm also referring to it over here. Is it going to work? What value are you adding by creating those page objects also? So there's a lot of discipline that is required to use this well. Again, similar thing over here. This is not even using the specific page object pattern. You're just from the intent itself, you're directly calling the pages, specific actions on the pages. These things do not scale. One more quick example, again moving back to Ruby Land. Let me know if you start feeling dizzy or something if you are switching context too much. But this is another very bad example where these are step implementations. So this was a cucumber example implemented in Ruby. A step is not only calling active record objects directly, the models directly. It is also calling the pages. It has so much logic embedded all throughout if you look at it. Worse of all, it also has sleep. How many of us use sleeps in a code? Don't hesitate. You need to get rid of it. Do you agree? Yes. Yes. It's okay to sleep but don't put sleep in the code. Okay? But this is another really poor example in that sense where you have all the complexities or rather the abstraction layers set up for you but you're not able to have the discipline in writing good code. How many of us have heard test code should be of production quality? Has anyone heard that? Okay, good few people. How many of us believe in that? For all those people who raise their hands, keep their hands raised. What do you do to ensure the test code is of production quality? For the design matters, that's fine. Same kind of code quality checks that happen for production code like static analysis or daily events of the code that is automated. Okay. So code checks, code reviews of sorts, right? Logging, performance. Logging, performance. How is performance going to help when you start in code quality? These types of production code you need to make sure they be fast enough to get exactly the same. And that's the reason why, so I'll just repeat what you said in a nutshell. So you want the test code to be fast of production quality to get the results quickly. That is the reason why we get into these problems is the first step in test code. Because we know test code is not going to production. That's why we take shortcuts and say, huh, this Ajax condition is very difficult to handle right now. Let me put in a sleep of five seconds. I know it always works in five seconds and I'll proceed. Antipatterns start off in such cases itself, right? So my request to all those who raise their hands is to find ways to do more to ensure test code is of good quality. Enough said about poor code examples. It's always fun to laugh on others' codes, though, isn't it? But now, to help keep code of good quality, what is it that we need to start doing? So first of all, you need to understand the principles of what is a test. What does it really mean by good code? Does good code equals good automated code or automation code not necessarily? How many developers in the room over here? Any developers? Few of them. How many developers contribute to writing test automation code? Okay, almost the same number of hands. How many developers use the same design patterns to implement test automation compared to what they would do in the production code? The number of hands has reduced for those who didn't see behind. Why? Because we want to get the product out to production, not the test code. But as you scale up your automation framework, that is where the problems start happening. For those who have more than 5,000 tests who had raised their hands, or more than 1,000, right? Does the team rely on the automation results solely for releasing to production? Then what's the value of those 5,000 tests? Let's do a Git delete branch. Because if the code is not adding value, it's useless, right? The point is we have to write maintainable code that will help validate the product functionality as quickly as possible. The feedback is so essential. So with the practices and principles in mind, and if we have time, I'll show those slides separately. I've hidden them off from the presentation. Then want to take the core content away. But our framework needs to get evolved. And how does it evolve? The same page object pattern, you have a test intent domain functionality actions verifications over here. You have pages in snippet formats, but then you start using entities and models of how do you really represent your test data? What type of data is flowing in between different pages, for example, when you're doing your validations? That starts becoming important. This sort of comes to the same web form pattern that Derek was talking about. How instead of saying I want to keep passing in five different parameters to create a user, I'm going to create a hash map or a dictionary of sorts and send that across, right? That dictionary is a model, representing user details, for example. There could be various types of models that you can create and reuse from your product functionality. That's where pairing with devs really help also. But then you start really building a framework in various different forms to say, okay, I need some helpers and utilities separately so that if I make one change in one place, everyone will be able to use it consistently. Loggers is important. Someone mentioned logging in terms of code quality, right? You need to know exactly what happens and how. Resource analysts, custom reporters, web service utilities, there might be various ways you can interact with the product, not necessarily directly doing browser actions. You might say that based on a browser action specific, URL is generated with certain parameters in headers or whatever. Let me just invoke those requests directly because not all tests need to go through the browser at all times. You have to find out various different ways how your testing can be more efficient, can give that feedback much more faster. And then if you have these various types of abstraction layers, that discipline is still very important because there's no one who can really say from the intent, who can prevent from the intent directly calling something on a web driver API. You need to have that discipline. You need to educate your colleagues and say why this is not the right way of doing things and what would be a better way of doing it. Just doing reviews is not going to be important or helpful. You need to find better ways to get that knowledge sharing happening. Again, I'm going to pick on the people who have got large frameworks. Who really maintains the framework? The one who has implemented it or someone else? Developers who maintain it, golden state, right? How many of us have seen that pattern before? It's different for various good patterns, it's different. But again, we are seeing various different anti-patterns, different ways of thinking, right? We need to be aligned what is important from a test framework perspective and then build it up on it. Now, with that, let's look at some implementations which actually make sense of a page-optic pattern. In this case, we just cannot see this part. I'm just going to read it out because the structure is what I was really hoping to share across. So, I've taken snippets of the page-object implementations from a specific repository and shown it in. This one is called partials. Partials means by itself, it doesn't do anything. It is part of some other page of sorts in various respected structures, right? And that's what it does. But if I look at that specific page, this has got just one element, how to browse. And that is sufficient. There is no overhead in really creating a new page object as long as that intent is really kept clear. So, this is where I don't agree necessarily, Derek, that it's okay to create page objects. Important thing is to keep that intent really separate. Otherwise, it's just a class. It doesn't matter. It's a matter of milliseconds to load the class in memory, right? So, that part is fine. But if you start reusing it, breaking it down in smaller chunks, it becomes more manageable. Now, would it be easy to have 20 such classes? For example, in this one, there are some more elements that you're interacting with. Or have that one class, which is 1000 lines long, which is going to be more easier to find than you reuse, right? That's the important aspect. It has to be easy for someone else to reuse what you have implemented. Otherwise, that person is going to start creating his or her own implementation. And now, you've got multiple ways of doing the same thing. How many of us have run into that problem, right? So, it's again important to understand the intent. If you have that intent really separated, it has to be atomic. Don't have dependencies in that intent. Or that implementation. That if it is coming from this sequence, only then this page will be, or this method will be called. That method has to be doing things independently. If it is not in the right context, throw an exception. That's a valid thing. The test has to fail if it is not doing the right thing. Okay? But that way, reusability will increase. So, there are a bunch of different things that are created over here. Now look at this class. A ton of different elements. And of course, some logic around it also. This class, not bad. 300 lines. 300 lines is not really bad. But, there's so many elements over here. And this is where the next problem starts coming in for page object pattern. We'll get into that problem shortly. Any questions so far? Do we understand the importance of page object patterns? Do we understand what problems can get into? Even if you're using page object patterns? Okay? So, page object pattern doesn't necessarily mean it's only for web pages. It's only for web-based products. Of course, that is where we see it mainly use it for mobile or mobile web, any web transactions. But I think it's equally applicable for desktop applications also. Anywhere where you have a workflow, and of course you won't be using web driver for Windows desktop application, for example. You'll use something else. But you have to logically break down that application that workflow into manageable units, into reusable units, reusable components, which will make that change very precise in that one location only. Only then, it will help out, okay? So, some tips for page object pattern. This is especially very valuable, where you have got a lot of data and a lot of locators in that page snippet that you're talking about. Like the other example I just showed you, right? Where there are a lot of elements. Please don't use getters setters. Again, contrary to Derek, what you mentioned in a certain way. Getters setters make the implementation very tightly coupled. If you change that element, product evolves, elements change, right? You still need to change the getter setter for that element throughout your framework. It doesn't remain a one place change. The locator might be just in the page, but will you say select item from checkbox, and if the checkbox has changed or dropped down, will you keep that name same of the getter setter or the variable? You'll adopt, you'll evolve it to match what the product is really doing, right? But what happens is the getter and setter name has changed as you adopt to it. The full framework wherever you're using it, that needs to change. It doesn't make sense. So what can be done is logically group those entities. Create user, classic example, the web form pattern that Derek was speaking about, right? It can be a simple dictionary, or you create models that we spoke about, entity models that can be a user, super user, admin user, unregistered user, whatever it might be, and have those details inside it. Create the test data in form of those models, and send those models across instead of using getters setters. So I could tell my create user page, create user with only mandatory information, that's the name of the API in the page itself, which will take some object, a dictionary or whatever, which has all the information required, for that page to enter information on the page, and return. Get me user information from a created user page. That's a good method name, which will return the similar type of object by scraping through that page completely. The page will do that, but none of the getters are exposed, none of the setters are exposed, none of the locators are exposed, even if some field changes from checkbox to drop down, only that page needs to change my logic of what does it mean by mandatory information has remained the same. Makes a huge difference in terms of keeping your intent concise, okay? Another very important aspect, handle weights. When I click on create user after entering all the data, it is going to take time for the next page to come up, or the next confirmation to come up. Wait for the confirmation to come up as part of that action itself. You don't want to say click submit, and then in some other place, okay, has submit really completed? What happens because of this? Whoever is calling click submit from hundreds of tests, they all need to implement the wait action for it. Instead, when you say click submit, wait, implement the waiting logic as part of that method itself, to say, yes, now I'm really done, and it could be a error case for that matter. If it's a error, I'm going to throw exception at that point, but everyone who calls that API will know exactly what is happening with it. No one else needs to know about that particular logic, okay? No headers and setters, wouldn't your framework could be becoming application sensitive? It would not be remaining generic in that case. Are you building a framework for any application? So that's a library that you're talking about, right? That's a utility's libraries which are generic. Click on element, that's a generic API. Click on submit button, that is submit to your application. Which internally we'll call? Extending that standard framework by building some kind of a models around it, which is business and economic. Exactly, and that's exactly where we are leading to, right? But before that, I'm just going to go back a little bit, what you're talking about. This common API that you're talking about, this is a generic library. That value knows how we apply the web driver, or web services, or HTML unit, or anything else. But whoever's calling this at the page level is saying, okay, click submit, so driver.clickElement, clickButton. Do that action, and then driver, get me a confirmation message. That's implemented in the page. You're still using the driver APIs to do the action. The waiting logic is part of the page implementation. Now is the page generic? No, it is tied to a domain or that product anyway. So the logic is not over here. It is at the page level. So I think this is one of the most crucial aspects if you look at it, even from a using of sleep perspective. This is where shortcuts might be okay to say, I've submitted something. I know I have to implement some action to intelligently wait for the results to come back. For now, I'm going to put a sleep of five seconds in this submit method, but no one else is using that. Everyone else is just transparent about its use. They're just saying submit. They don't care about sleep. And then when you get a chance, hopefully you get a chance, you come back, implement the logic to say, how do I intelligently wait for it? Again, no one place change. So what are the limitations that we see out of page object pattern? One is a test intent gets polluted. The intent is to create a patient. But in creating a patient, so if I take a JUnit Java example, there's a test annotation for JUnit test annotation and you'll say, okay, I home page, new home page. Home page, login. From there, go to patient registration page. Home page, go to patient registration page, which returns to me a patient registration page object. And do that flow completely. What is the intent of that test? What you start seeing is, so what you start seeing in this case, is the name of the test should add new patient. I go to a, this is my data object that I've created a model. I go to my registration page. From registration page, I say register patient with basic information, come to a chart page, and then do some assertion. Does anyone really understand what the true intent of this test is? What I see is a user flow, a UI flow that is represented. What is the business value of this test? But now you are trying to read through the UI flow to understand what the business challenge is. Challenge starts coming over there. Eventually you're left with code which is doing something, but you're not sure what that validation really is happening out of it. So the test intent gets polluted. Because of this, you now start seeing, one test is saying should add new patient with mandatory information, another test is doing with all information. Now the test intent, implementation of that intent also starts getting duplicated in certain ways, because in one test, I've already done the same set of actions. I have to copy paste, change some parameters out of it, maybe add or reduce some things also, and that's my new intent. So I have duplicated. By the way, one of the things that I hate the most, which I think should be a rule, the control C, control V should be disabled in the IDs. That causes the biggest pain in test frameworks, because it's easy, it's shortcut, right? Copy paste a block, change certain things, and my test is passing. The intent becomes imperative. So you start thinking in terms of the user actions or the UI actions, not in terms of the business value, what the test is really trying to do. As a result, we've already spoken about the maintenance challenges and the scaling challenges. As the test really grows, what happens? So why are we doing automation, right? Everyone heard, anyone not heard about the test automation pyramid? Everyone heard about it? Okay. So very quickly, the test automation pyramid says that there could be various different types of tests that are applicable for your product. These are for reference only, right? Starting right from unit test all the way up to UI, and of course you cannot exclude manual exploratory testing. All this is a effort of, or rather, will give you a sense of quality of that application that you're building. Now what happens is, as you go from bottom of the pyramid to the top, the cost of building those tests increases, the time it takes to execute those tests increases, and the value also has to increase, right? Because you're not talking about granular functionality, you're talking more and more, bigger functionality of the application. What this does not show in the projector is really horrible in this case, but there should be, imagine there's an inverted triangle behind this. It is there, I can see it over here, it's just not visible here. So there's an inverted triangle, or inverted pyramid, rather, behind this automation pyramid. What it represents is a unit test touches upon the most granular pieces of the functionality of your product. As you start going up the test pyramid, integration test, JavaScript test, view test, web service test, it needs more and more of the application to have come together in order to validate that, okay? Everyone clear with this concept of the pyramid? So now, how many of us write a Selenium-based test, a functional regression test, to check specific elements are present or not, for example. Or specific labels are present or not in the UI, okay? Are they really functional regression test? It's just a UI test, right? I would actually classify those tests as view test. They are important, I'm not saying they're not important. They're important, but you just need to load up that one specific page, check if those conditions are met or not, and exit. You don't need to build complex flows out of it, scenarios out of it, to see if those tests are working or not. These are view tests, they are not end-to-end tests. What really happens is, up to the view test, whatever test you write, they are more technology-facing side. I change a checkbox to a dropdown. That's a view test. It's a UI test, but it's a view test. It's technology change. What is the impact of changing it from a checkbox to a dropdown? Why are we even doing that, just because someone likes one control better than the other? No, there has to be a reasoning behind it, right? There has to be a business reasoning why that change has been required. And those are the types of tests that we should be looking at as business-facing tests. What is the impact of changing a certain locator in a certain way? Is it really increasing usability? Is it really taking the user till the end state and actually placing the order? We have to figure these out. So what type of test you automate at which layer needs to be really thought of. So for all those who have got more than 500 or 1000 tests, think about how can you delete 50% of those tests and still get the same return of investment? Because I'm very sure those 5000 tests are not really serving the business-facing test. It's just because there might be a limitation of number of tests at the lower layers, you've tried to automate that on the top, okay? So now the intent of selling them as I see it best, what we should be writing as testers, is business-facing tests. We need to ensure that why are we writing this test? Does it make sense for business? How are we validating the business functionality? With that intent in mind, I updated the architecture diagram, my framework diagram to say, I'm going to separate the test intent from the business layer functionality, my domain functionality. My intent is purely about what and why am I validating? To do those validations, I'm going to perform some business operations. Those business operations may call the business operations, because they could be complex operations, or they could be calling pages, and of course there are entities, resources, everything else remains the same. We've just separated these top two layers in different forms. How does that help? Let's look at another quick code example. So this is a Cucumber JVM example that I'm using in this case, where I'm saying, this is a sample repo that I created to just experiment with Cucumber JVM, but what I'm saying in terms of intent over here, when I'm on the search page, when I search for certain content, I should see a list of related posts, nothing really business logic in it. This was a simple example. As you would see in any Hello World examples, or anything that you search online, nothing is going to be of the complexity that you are really working on. They're all simple Hello World examples. You have to set the baseline really low, in that sense, right? But what we do over here is, if you specify your intent in business language, what are you doing? I'm not saying I enter certain item in the checkbox. I'm not saying I click on the search button. I'm saying I search. Search is my domain terminology over here, right? I see a list of related posts. Now list, maybe you can argue is it a UI centric or is it actually a domain centric thing? But once you see these kind of things, once you specify the business intent, then in your actual code, I have what is called a business layer, which is definitely not readable over here. There's a business layer, which is great. Then there are pages, utilities below that. That's a folder structure. But now, in my business layer, all I have is business operations. I search for certain things, and I assert certain things. My pages are being called from the business layer. My intent has remained very crisp and clear about what the business value is. My business operations will internally, because this is now classic code, right? It is not part of my test annotation or any JUnit annotations, unit annotations. It is all in classic code itself, first level objects. I could do refactoring very easily. I say instead of search for a string of search criteria, I'm searching for complex operations. I could create a new API over here, which my steps would call, or my intent would call, and do whatever refactoring is required at the implementation level to reuse functionality. Then this will call the pages itself. In my pages, if you see, for example, get number of search results. I find the existing search results. I take screenshots over here, whatever is required, do logging, and then return a certain value. This is a storage level. Now, this is not just doing a getter and setter. It has some more logic around it in terms of screenshots. Why? Because screenshots are very important from a functional testing perspective in order to trace back what has really happened. It is okay to put those kind of things over here. Okay? Find element. In this case, there's no Ajax really, so I don't need to wait for anything. Others, I would have waited for certain things to happen at this point in time. This is another good example, search for. In a page, I'm saying search for, I'm not saying enter search criteria and something. In search for, I take in a parameter, what is to be searched. I do that search operation, and I click the search button also. There's no need to say enter search criteria, click on search. It's going to make my test again more imperative. Not saying that should not be done at all, but look very consciously. Do you really need to expose something, some elements as getters setters? In some cases, it's a valid reasoning why you need to do that. But I would rather go that route if there is no other better solution in mind. Okay? Does it make sense? Yeah? So, what are the advantages of business layer page object pattern? The most important thing is your intent becomes very important and crisp and clear. You're validating what the business, your product, managers really want. All other forms of tests are validating technology. These tests, they are validating what is important from a business perspective. The test pyramid remains sane. At top of the pyramid, you don't start going into anti-pattern of an ice cream cone, where my functional tests have become 5,000 and my unit tests have remained 1,000. Okay? The abstraction layers allow separation of concerns. You put the logic exactly where it is required so that it truly remains a single place of change. If an intent changes, a business layer needs to change. If my business layer has changed, maybe it's just the way the pages are interacting with each other has changed, or maybe the pages itself have gone some change also. But you know exactly where to make the change. You're not going all over the place for that. The changes are isolated as a result if you really follow the abstraction layer as well. Okay? And as a result, maintenance and scaling becomes easier. I'm not saying it solves the problem, but it becomes much more easier. Okay? Some of the best practices very quickly is use proper abstraction layers, pages as dummy objects. Dummy in the sense, it doesn't have any assertions inside it. It knows how to get and set information. As part of setting information, it is just handling the weight. That is the extent of logic that you're putting in. But you're not saying log in, oh, now throw assertion error if at that point, if login failed, for example. Assertion is all at the business layer. Business layer understands if I should have successfully logged in or if login should have failed. What is the intent that is going to drive the business layer? Pages are just going to say enter login details, enter, click on login, and get the error message if any. Model test data, this is so important. Do I want to use dictionary? Do I want to directly use the test data in the test itself or separate property files? Or do I want special objects created? Think about it. Complex applications needs those kind of thoughts. Assertions and validations in the business layer, very important. Don't mix that at the page level. Pages don't know anything about it. The only exceptions that pages will throw if element is not found, for example, or timeout type of exception. Business validation errors should be at business layer itself. Identify intent in business terminology. So when you specify the intent, don't say I go to this page and then I do this. What is the value of really automating that? That is very important. I should be able to create a user of a specific type. Why that is going to be important? That is very crucial to get in the intent side. Use, build, tools, utilities as appropriate. We are in an open source world where there's so much information available to us. Let's reuse as much as possible. No need to reinvent the wheel. At the same time, don't hesitate to build something or extend something which is important to you which no one else has done before. That is okay. Evolve the framework. I always like to give this example that this is a construction industry example. If someone has to build a bridge, what is the first thing that happens? A set of engineers, architects, they would go and survey the topology and see what type of terrain it is, wind conditions, various different factors go into it. Based on that, a blueprint is created. What is the bridge that is we want to build over here? Suspension bridge of any other type of bridge. But then, after the blueprint is created, do you start off by ordering the street lamps and the paint that is required for the bridge? You start with the foundation. At the same time, you would start thinking about, oh, if I need to order specific type of lights which has a lead time of six months, I better plan it in advance so I get it in time. But you don't necessarily start off with that. So evolve your framework in the same fashion. Have a vision of what the framework needs to do based on the context that you are in. Context is the product that is under test, the distribution, the way of working, the skill set of the people, so many different factors over there. If cost is a concern, then should you go open source or commercial tools? So many factors. But evolve the framework once you have that vision in mind. Use proper programming practices. This is so important. Test code should be of production quality. Read about it, design patterns, fantastic book. Read that. I don't know how many testers have done that. Use proper programming concepts, not just patterns, use proper programming concepts. By default, don't make any method public, for example. Start off with private as the accessor level and then, oh, wait a minute, maybe some subclass is wanted so I'm gonna make it protected. Oh, wait a minute, this actually is supposed to be a public method, then I'll make it public. Why exposing the intent out to everyone which is going to cause pains later if you have to change those APIs, right? Incapsulation, such an important thing. And we already spoke about design patterns. Some of the references that I have used over here, page objects from Google, code Google com. Martin Fowler has written a very nice simple blog about page objects. Very easy to read and understand. Definitely read that. And there's also test design considerations which I happen to find on Selenium HQ which talks about what types of, how should you really build your pages, assertions, where should that be, those kind of details. With that, I have come to the end of this. I'm open for any more questions. Since you have spoken and interacted along the way, I don't want to hold up lunch also too much. One more? No. These slides I'll be putting up, PDF versions will also be available on Slideshare and also I think the conference website will have links to audios and the slides and videos also. Yeah, it should be available. But first case, just contact me and I can send you a link to Slideshare if required. Any other questions, thoughts, comments? Yeah? Different people with design, they're pretty much in its own way. Company, code, Selenium, FJ, with Selenium, test engine, and then, Sokomba, it's made on the model. It's made on the model with Selenium and Selenium and Java. Yeah, what else? We have to connect with all of them. Let's have a discussion separately about this because there's a completely different discussion on what type of cool sets and of course we need to have for all of these sets. Right? This is really about why page object is important but it's also limiting as a scale use of a product. But let's have a conversation after this session and we can talk more details about it. Thank you.