Basic refactoring to use PageObjects with Appium (using .NET)

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 –


[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 –


[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");
}

view raw

Page.cs

hosted with ❤ by GitHub

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 ….


AppiumWebElement TradeMe_Login = app.FindElementById("nz.co.trademe.trademe:id/editTextLoginEmail");
TradeMe_Login.SendKeys("xxxxxx@gmail.com");

view raw

1.cs

hosted with ❤ by GitHub

We are just calling a method that does that for us , with worrying about which locator/element to call


Trade_Me_PageObject.Enter_UserName("xxxxxx@gmail.com");

view raw

2.cs

hosted with ❤ by GitHub

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.


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);
}

view raw

PageObject.cs

hosted with ❤ by GitHub

The above methods and elements were then consumed by the NUnit test


Trade_Me_PageObject.Enter_UserName("xxxxxx@gmail.com");

view raw

consume.cs

hosted with ❤ by GitHub

To Do/Debt –

  1. 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 😉
  2.  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

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s