What is PageObject model(POM) ?
POM is a technique to hide some of the details of interacting with Web & Mobile elements, so that writing tests and reading code is easier.
Here is a good starter resource for understanding POM for Appium – https://blog.testproject.io/2017/07/16/page-object-model/
POM in action –
I want to use this blog post to show a before vs after comparison of using POM , thus highlighting the effectivness of it ( in terms of achieving code abstraction) and hence reducing the cognitive load on someone writing the test .
Before state –
I wrote couple of tests using NUnit to loginto the TradeMe Android app and confirm that the expected user name is being displayed (on successful logon).
Here are the guts of the test –
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[Test()] | |
public void TradeMe_verify_logged_in() | |
{ | |
// Find the login input field | |
AppiumWebElement TradeMe_Login = app.FindElementById("nz.co.trademe.trademe:id/editTextLoginEmail"); | |
// key in your user name | |
TradeMe_Login.SendKeys("xxxxxxx@gmail.com"); | |
// Find the password field | |
AppiumWebElement TradeMe_Pwd = app.FindElementById("nz.co.trademe.trademe:id/editTextLoginPassword"); | |
// key in the password | |
TradeMe_Pwd.SendKeys("xxxxxxxx"); | |
// Find the login button | |
AppiumWebElement Login_Button = app.FindElementById("nz.co.trademe.trademe:id/buttonLogin"); | |
// Tap the login button | |
Login_Button.Tap(1, 1); | |
// Wait for the account menu item to be displayed | |
WebDriverWait wait = new WebDriverWait(app, TimeSpan.FromSeconds(30)); | |
wait.Until(ExpectedConditions.VisibilityOfAllElementsLocatedBy(By.Id("nz.co.trademe.trademe:id/menuAccount"))); | |
// Find icon for my account | |
AppiumWebElement account_icon = app.FindElementByAccessibilityId("Account"); | |
// tap the icon | |
account_icon.Tap(1, 1); | |
// confirm that the logout button is visible, this is in turn ensure that the user name isbeing displayed | |
wait.Until(ExpectedConditions.VisibilityOfAllElementsLocatedBy(By.Id("nz.co.trademe.trademe:id/buttonLogout"))); | |
// find and assert on the text of the user name | |
AppiumWebElement account_name = app.FindElementById("nz.co.trademe.trademe:id/textViewAboutMemberUsername"); | |
Assert.AreEqual(account_name.Text, "xxxxxx"); | |
} |
After state –
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[Test()] | |
public void TradeMe_verify_logged_in_POM() | |
{ | |
// Use a Page Object method to enter user name | |
Trade_Me_PageObject.Enter_UserName("xxxxxx@gmail.com"); | |
// Use a Page Object method to enter password | |
Trade_Me_PageObject.Enter_Password("xxxxxx"); | |
// Use a Page Object method to press log in button | |
Trade_Me_PageObject.Tap_LoginButton(); | |
// explicit wait .. this still needs to be refactored 😉 | |
WebDriverWait wait = new WebDriverWait(app, TimeSpan.FromSeconds(30)); | |
wait.Until(ExpectedConditions.VisibilityOfAllElementsLocatedBy(By.Id("nz.co.trademe.trademe:id/menuAccount"))); | |
// Use a Page Object method to click account icon | |
Trade_Me_PageObject.Tap_AccountIcon(); | |
// another explicit wait… this needs to be refactored too | |
wait.Until(ExpectedConditions.VisibilityOfAllElementsLocatedBy(By.Id("nz.co.trademe.trademe:id/buttonLogout"))); | |
// perform an assert on the account name returned by the Page Object class | |
Assert.AreEqual(Trade_Me_PageObject.Return_AccountName_UnderTest(), "sunjeet81_1"); | |
} |
Key difference –
In the after state all the locator related code has been abstracted away into the Page Object class.
So, rather than saying
Find me this element and type some text in it ….
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
AppiumWebElement TradeMe_Login = app.FindElementById("nz.co.trademe.trademe:id/editTextLoginEmail"); | |
TradeMe_Login.SendKeys("xxxxxx@gmail.com"); |
We are just calling a method that does that for us , with worrying about which locator/element to call
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Trade_Me_PageObject.Enter_UserName("xxxxxx@gmail.com"); |
Imagine this being repeated for each element and it’s operation across your tests, makes for significant gains in terms of code readibility
How did I abstract the detail out ?
I did some background reading
here –> https://github.com/appium/appium-dotnet-driver/wiki/Page-objects
and looked a some code samples here —> https://github.com/appium/sample-code/tree/master/sample-code/examples/dotnet/AppiumDotNetSample
All the element locator related info was moved to another class. Operations (e.g. entering text or getting text of an element) were exposed as methods by that class.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Trade_Me_logon | |
{ | |
// Define the element and how will we find it ? | |
[FindsBy(How = How.Id, Using = "nz.co.trademe.trademe:id/editTextLoginEmail")] | |
IWebElement LoginField { get; set; } | |
// Do some action on the element | |
public void Enter_UserName(string text) | |
{ | |
LoginField.SendKeys(text); | |
} |
The above methods and elements were then consumed by the NUnit test
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Trade_Me_PageObject.Enter_UserName("xxxxxx@gmail.com"); |
To Do/Debt –
- As you might have observed in one of the code snippets above,I have to still refactor the explicit wait related code . Need to do some research on how to do it 😉
- For the same element, I am using Interface “IWebElement” in the Page Object class vs “AppiumWebElement” in my original NUnit test . The reason I had to do it because using “AppiumWebElement” type in the Page Object class used to throw an exception when called from the test. I still have to figure out why !?
Leave a Reply