How to identify, create and choose when to use a PageObject, inner PageObject, PageObject method abstractions, and extension methods.
How two or more PageObjects interact with each other.
An example to create PageObjects from a web browser.
The completed sample solution described in this article can be downloaded here:
Before this guide
This guide assumes that you have read the Basic Concepts and Page Object articles. Also, we'll be using the following starter solution throughout this guide:
Identifying PageObjects
Open the starter solution QualityMatePageObjectExample.sln and the POExample.html file in a web browser. This will be our example web application.
We can represent the example web application with the following diagram. Doing this helps us keep in mind the different parts that make up the application under test; thus, creating each PageObject will be easier.
Creating a PageObject
As mentioned in the PageObject article, PageObjects can represent an entire web page. We can define our first PageObject using the example web application as RegisterPage and this will represent our page.
Now we can populate our PageObject with the controls from the page (inputs, tabs, buttons, etc.). But beware: if we decide to populate RegisterPage with all the controls, we might end up with a class too big to maintain.
Creating an Inner PageObject
Instead of have a class that handles all controls within a page, we can abstract pages or group of controls into an Inner PageObject.
Inspecting POExample.html, we can create Inner PageObjects for each Tab (User Information and Contact information), this will reduce the RegisterPage control count and responsibilities.
With both Inner PageObjects created, we'll add a reference to RegisterPage to these Inner PageObjects and any other controls that are responsibility of RegisterPage. The code should look like this:
Now we have completed the proposed diagram with all the PageObjects.
PageObject Methods
Since PageObjects are C# classes, they may also contain methods that interact with their own controls or inner PageObjects. This reduces code duplication and separates responsibilities related to the PageObject in use.
To continue with our example application, each PageObject class (UserInformationTab and ContactInformationTab) could have their own method to fill the input fields they contain.
First, we'll declare the following structs that handle the data which will be passed to the PageObject.
We will also add the PageObjects methods that will use the struct data and write it to the specific control.
[Selector("#Tab1",SelectorScope.Root)]publicclassUserInformationTab:PageObject{ /// The controls already definedpublicvoidFillUserInfo(UserInformation userInfo) {this.Name.Text=userInfo.Name;this.Email.Text=userInfo.Email; }}
[Selector("#Tab2",SelectorScope.Root)]publicclassContactInformationTab:PageObject{ /// The controls already defined publicvoidFillContactInfo(UserContact contact) {this.Address.Text=contact.Address;this.City.Text=contact.City;this.State.Text=contact.State;this.Phone.Text=contact.Phone;this.Subscribe.Checked=contact.Subscribe; }}
With both of these methods at hand, it only makes sense to extend the idea to RegisterPage, which can have a method that calls the Inner PageObject methods. From our example application, it only needs to pass the respective data (using a struct called UserData) and click the submit button.
[Selector("body",SelectorScope.Root)]publicclassRegisterPage:PageObject{ /// The controls and Inner PageObjects already definedpublicvoidAddUser(UserData info) {this.Tab1.Click();this.UserInformationTab.FillUserInfo(info.UserInformation);this.Tab2.Click();this.ContactInformationTab.FillContactInfo(info.UserContact);this.Submit.Click(); }}
Create a test case
Finally, we should use these methods in a test case. For this example, we want to check if the submitted data is submitted as expected. Since the main topic of this article is PageObject, we can replace the code used for the test case script in MyTestClass.cs file for the following:
privatevoidMyQualityMateScript(UiExecutionController controller){ // Get the PageObjetRegisterPage myPageObject =controller.GetPageObject<RegisterPage>(); // Creation of UserInformationUserInformation information =newUserInformation("QualityMate","qualitymate@mobilize.net"); // Creation of UserContactUserContact contact =newUserContact("Costa Rica","San Jose","San Jose","512-243-5754",true); // Creation of UserDataUserData data =newUserData(information, contact); // Use the method from RegisterPage PageObjectmyPageObject.AddUser(data); // Do validates of the UserDatamyPageObject.Results.Validate(self =>self.Text.Contains(information.Name)&&self.Text.Contains(information.Email)&&self.Text.Contains(contact.Address)&&self.Text.Contains(contact.City)&&self.Text.Contains(contact.State)&&self.Text.Contains(contact.Phone)&&self.Text.Contains(contact.Subscribe.ToString().ToLower()) );}
Remember to load the QualityMateSettings.runsettings file on your IDE. If you're working on Visual Studio, information on how to do this can be found here.
What's next?
Compile the test project and run the test case to see the magic happen. Great isn't it?