Enroll in Selenium Training

In the last chapter of PageFactory in C#, we have learned that how to Design the Page Object Model using Selenium PageFactory. We learned that how beautifully we can design our PageObject classes and how easy and maintainable the automated test can be after implementing pagefactory.

But as told earlier that PageFactory instantiates all the elements of the web page at the start when we Initialized any page class objects. But think of the elements which will display on the web page after some action, say Ajax action. In PageFactory, every time when we call a method on the WebElement, the driver will go and find it on the current page once again. In an AJAX-heavy application, this is what we would like to happen. So, in that case, we are good to use the PageFactory as if the element appears later, the pagefactory will still look for the element on its turn.

But in the case of regular application where elements are stable and when we know that the element is always going to be there and won't change.  It would be handy if we could 'cache' the element once we'd looked it up and save some time of execution by commanding PageFactory to not search the WebElements on the page again.

PageFactory CacheLookup

PageFactory also provide CacheLookup attribute to cache the WebElements. This attribute helps scripts to instruct the InitElements method to cache the element once it's located. In other words, any attribute marked [CacheLookup] will not be searched over and over again – this is especially useful for elements that are always going to be there (not always true for AJAX apps). It means the elements of the page will be cached once searched. All elements used in the HomePage & LoginPage class are static and are always present. So it is better to cache objects and save execution time of the test run.

How to use PageFactory CacheLookup

If we know that element is always present on the page, it is best to use the following declaration:

[FindsBy(How = How.Id, Using = "account")][CacheLookup] public IWebElement MyAccount { get; set; }

PageFactory CacheLookup NameSpace

PageFactory functionality resides in OpenQA.Selenium.Support.PageObjects.CacheLookupAttribute.

How to Implement PageFactory CacheLookup in Framework

We will be using the same LogIn Scenario for this. Moving further as well we will keep on enhancing this scenario and soon will reach to the end goal of our Selenium Automation Framework.

Change Page Objects

All we need to do here is to add PageFactory CacheLookup attribute for every element. In HomePage class we just have one element so far.

HomePage PageObject Class

using OpenQA.Selenium;
using OpenQA.Selenium.Support.PageObjects;

namespace OnlineStore.PageObjects
{
    class HomePage
    {
        private IWebDriver driver;

        [FindsBy(How = How.Id, Using = "account")][CacheLookup]
        public IWebElement MyAccount { get; set; }
    }
}

LoginPage PageObject Class

using OpenQA.Selenium;
using OpenQA.Selenium.Support.PageObjects;

namespace OnlineStore.PageObjects
{
    public class LoginPage
    {
        private IWebDriver driver;

        [FindsBy(How = How.Id, Using = "log")]
        [CacheLookup]
        public IWebElement UserName { get; set; }

        [FindsBy(How = How.Id, Using = "pwd")]
        [CacheLookup]
        public IWebElement Password { get; set; }

        [FindsBy(How = How.Id, Using = "login")]
        [CacheLookup]
        public IWebElement Submit { get; set; }
    }
}

Test for LogIN Functionality

The test case will remain same and stays as it is. This is the benefit of following Page Object model that if there is any change in your PageObject, test remains same.

LogInTest.cs TestCase

using NUnit.Framework;
using OnlineStore.PageObjects;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.Support.PageObjects;

namespace OnlineStore.TestCases
{
    class LogInTest
    {
        [Test]
        public void Test()
        {           
            IWebDriver driver = new FirefoxDriver();
            driver.Url = "https://www.store.demoqa.com";

            var homePage = new HomePage();
            PageFactory.InitElements(driver, homePage);
            homePage.MyAccount.Click();

            var loginPage = new LoginPage();
            PageFactory.InitElements(driver, loginPage);
            loginPage.UserName.SendKeys("TestUser_1");
            loginPage.Password.SendKeys("[email protected]");
            loginPage.Submit.Submit();
        }
    }
}

Project Explorer

Your Project explorer window will look like this now.

PageFactory CacheLookup

Notes

  1. If you use the PageFactory, you can assume that the fields are initialised. If you don't use the PageFactory, then NullPointerExceptions will be thrown if you make the assumption that the fields are already initialised.
  2. List<WebElement> fields are decorated if and only if they have @FindBy or @FindBys annotation. Default search strategy "by id or name" that works for WebElement fields is hardly suitable for lists because it is rare to have several elements with the same id or name on a page.
  3. WebElements are evaluated lazily. That is, if you never use a WebElement field in a PageObject, there will never be a call to "findElement" for it.

Advantages

  1. When the PageFactory is initialised the proxies are configured, but the WebElements are not found at that point (so you won't get a NoSuchElementException).
  2. Every time you use a WebElement it will go and find it again so you shouldn't see StaleElementException's.
  3. But when you use the @CacheLookup annotation, which is losing you the second benefit as it will find the element once and then keep a reference to it, you are now far more likely to see StaleElementExceptions.

Differences between C# and Java Implementation

  1. In C# the PageFactory.InitElements returns void, whereas in Java it returns the Page Objects.
  2. The PageFactory implementation for C# only searches for elements using the ID. It does not locate the elements using the NAME property. Whereas in Java it also tries to find the element with Name property, if it is not able to find it with ID.
  3. The Java implementation can locate the element even without the FindsBy attribute. This isn’t the case for C#.
PageFactory in C#
PageFactory in C#
Previous Article
Optimizing Page Object Model
Optimizing Page Object Model
Next Article
Lakshay Sharma
I’M LAKSHAY SHARMA AND I’M A FULL-STACK TEST AUTOMATION ENGINEER. Have passed 16 years playing with automation in mammoth projects like O2 (UK), Sprint (US), TD Bank (CA), Canadian Tire (CA), NHS (UK) & ASOS(UK). Currently, I am working with RABO Bank as a Chapter Lead QA. I am passionate about designing Automation Frameworks that follow OOPS concepts and Design patterns.
Reviewers
Virender Singh's Photo
Virender Singh

Similar Articles