文档章节

Selenium PageObjects and PageFactory

红焖鲤鱼
 红焖鲤鱼
发布于 2016/03/24 15:38
字数 1545
阅读 153
收藏 0

PageObject Design Pattern

The PageObject design pattern models areas of a UI as objects within test code. The functionality classes (PageObjects) in this design represent a logical relationship between the pages of the application. Each class is referred to as a PageObject and returns other PageObjects to facilitate the flow between pages. Because PageObjects are returned, it becomes necessary to model both successful and unsuccessful events that can occur when interacted with a page.

For example, consider logging into Gmail. After entering the user details, the step either passes and navigates to the Inbox page or stays on the Login page possibly due to invalid input parameters. A pass would then return the Inbox PageObject whereas a fail would return the Login PageObject.

This means better tests, exception handling and reporting. It may sound a little confusing, but its quite a simple yet an elegant approach to write your tests. Let’s break down the above explanation into actual PageObjects.

In the code below, the following events occur:

  1. Constructor verifies if page is valid

  2. Attempts login to the Flights application

  3. (If successful) Returns the FindFlights PageObject

class LoginPage{
    private IWebDriver driver;     public LoginPage(IWebDriver driver)
    {
        this.driver = driver;         // 1. verify if page is valid
        if (driver.Title != "Welcome: Mercury Tours")
            throw new NoSuchWindowException("This is not the Login page");
    }     // return FindFlightsPage PageObject
    public FindFlightsPage Do(string UserName, string Password)
    {
        // 2. steps to login to the Flights application
        driver.FindElement(By.Name("userName")).SendKeys(UserName);
        driver.FindElement(By.Name("password")).SendKeys(Password);
        driver.FindElement(By.Name("login")).Click();         // 3. return FindFlights PageObject
        return new FindFlightsPage(driver);
    }}

For the above to work, the FindFlightsPage PageObject must be created. In the code below, the FindFlightsPage PageObject is modeled to perform the following actions:

  1. Constructor verifies if page is valid

  2. A method is called to find a flight

  3. The Logout method is called and actions performed

  4. Return the LoginPage PageObject

class FindFlightsPage{
    private IWebDriver driver;     public FindFlightsPage(IWebDriver driver)
    { 
        this.driver = driver;         // 1. verify if page is valid
        if (driver.Title != "Find a Flight: Mercury Tours:")
            throw new NoSuchWindowException("This is not the FindFlights page");
    }     // 2. method/code-block to find a flight
    public void Do()
    {
        Console.WriteLine("In FindFlightsPage.Do [Checking for Flights]");
    }     // returns LoginPage PageObject
    public LoginPage Logout()
    {
        // 3. log-off and return to LoginPage
        driver.FindElement(By.LinkText("SIGN-OFF")).Click();
        driver.FindElement(By.LinkText("Home")).Click();         // 4. return the LoginPage object
        return new LoginPage(driver);
    }}

Main execution entry point (the test code) below.

using OpenQA.Selenium;using OpenQA.Selenium.Firefox;using OpenQA.Selenium.Support.UI;using System; class Program{
    static void Main()
    {
        // instantiate FirefoxDriver and navigate to NewTours flight app
        IWebDriver driver = new FirefoxDriver();         // navigate to NewTours app
        driver.Navigate().GoToUrl("http://newtours.demoaut.com");         // instantiate LoginPage
        LoginPage Login = new LoginPage(driver);                 // Login.Do returns the FindFlightsPage PageObject
        FindFlightsPage FindFlights = Login.Do("test", "test");         if (FindFlights != null) 
        { 
            // perform steps to find a flight
            FindFlights.Do();              // FindFlights.Logout returns LoginPage
            Login = FindFlights.Logout(); 
        } 
        Console.ReadLine();
        driver.Quit();
    }}

As we saw above, the Login.Do(args) method returns the FindFlights PageObject whereas theFindFlights.Logout() method returns the LoginPage PageObject. We saw that the public methods of each class represent the functionality offered by the page. The real-world application of this concept will certainly contain more actions against the UI and may return a large number of PageObjects.

PageFactory Class

The PageFactory Class is an extension to the PageObject design pattern. It is used to initialize the elements of the PageObject or instantiate the PageObject itself (not in C# though – see the Notes section below). Annotations for elements can also be created (and recommended) as the describing properties may not always be descriptive enough to tell one object from the other.

The InitElements method of PageFactory initializes the elements of the PageObject. The code below shows PageFactory usage in detail.

using OpenQA.Selenium;using OpenQA.Selenium.Firefox;using OpenQA.Selenium.Support.PageObjects; // *using System; class LoginPage{
    private IWebDriver driver;     [FindsBy(How = How.Name)]
    private IWebElement userName; // How.NAME = userName     [FindsBy(How = How.Name)]
    private IWebElement password; // How.NAME = password     [FindsBy(How = How.Name)]
    private IWebElement login; // How.NAME = login     public LoginPage(IWebDriver driver)
    {
        this.driver = driver;
    }     public FindFlightsPage Do(string UserName, string Password)
    {    
        userName.SendKeys(UserName);
        password.SendKeys(Password);
        login.Click(); 
        PageFactory.InitElements(driver, (new FindFlightsPage(this.driver)));
        return new FindFlightsPage(driver);
    }} class Program{
    static void Main()
    {
        IWebDriver driver = new FirefoxDriver();
        driver.Navigate().GoToUrl("http://newtours.demoaut.com"); 
        LoginPage Login = new LoginPage(driver);         // initialize elements of the LoginPage class
        PageFactory.InitElements(driver, Login);
        // all elements in the 'WebElements' region are now alive!
        // FindElement or FindElements no longer required to locate elements 
        FindFlightsPage FindFlights = Login.Do("User", "Pass");
        driver.Quit();
    }}

The WebElements userNamepassword and login are not explicitly defined using property-value pairs. However, if you execute the code above, ‘UserName’ and ‘Password’ strings will be supplied to the relevant text fields. The WebElement variable names were enough to identify the controls.

In the above example, PageFactory.InitElements facilitates searching for elements marked with the FindsByattribute by using the NAME property (notice: How = How.Name) to find the target element. There are other ways of object identification though and it is not required to use the object property as the variable name to identify it (as shown next).

The How parameter of FindsBy attribute is used for the object property (html tag). Using= then defines the corresponding value of the How= parameter.

Until now, NAME property has been directly used as the variable name. This is not very flexible approach, and I was only using it to provide a quick overview. Annotations are possible, too. The WebElements can be defined by any descriptive name. In the code below, userName, password and login have been modified to txtUserName, txtPassword and txtLogin respectively.

class LoginPage{
    private IWebDriver driver;     [FindsBy(How = How.XPath, Using = "//input[@type='text' and @name='userName']")]
    private IWebElement txtUserName;     [FindsBy(How = How.Name, Using = "userName")]
    private IWebElement txtPassword;     [FindsBy(How = How.Name, Using = "login")]
    private IWebElement btnLogin;     public LoginPage(IWebDriver driver)
    {
        this.driver = driver;
    }     public FindFlightsPage Do(string UserName, string Password)
    {
        txtUserName.SendKeys(UserName);
        txtPasswowrd.SendKeys(Password);
        btnLogin.Click();         return new FindFlightsPage(driver);
    }}

In summary, PageFactory class can be used to initialize elements of a Page class without having to useFindElement or FindElements. Annotations can be used to supply descriptive names of target objects in the AUT to improve code readability. There are however a few differences between C# and Java implementation – Java provides greater flexibility with PageFactory (see Notes).

CacheLookup

One last thing that remains with PageFactory is the CacheLookupAttribute. This is important because it can be used to instruct the InitElements method to cache the element once its 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). So, we can search once and cache. All elements used in this article can be defined by this declarative tag as they are static and are always present. Our LoginPage class then becomes:

class LoginPage{
    private IWebDriver driver;     [FindsBy(How = How.Name)][CacheLookup]
    private IWebElement userName;     [FindsBy(How = How.Name)][CacheLookup]
    private IWebElement password;      [FindsBy(How = How.Name)][CacheLookup]
    private IWebElement login;      public LoginPage(IWebDriver driver)
    {
        this.driver = driver;
    }     public FindFlightsPage Do(string UserName, string Password)
    {
        userName.SendKeys(UserName);
        password.SendKeys(Password);
        login.Click();         return new FindFlightsPage(driver);
    }}

Notes – Differences between C# and Java Implementation

There are 3 discrepencies I found in the PageFactory documentation at Google Code between the Java and C# implementation.

The first discrepancy is that in Java, the PageFactory.InitElements can return the PageObject. In C#, this is not the case as InitElements returns void. View this image for a snapshot from Google Code documentation showing Java returning the PageObject.

For the 2nd discrepancy, let’s refer to the documentation:

… It [PageFactory] does this by first looking for an element with a matching ID attribute. If this fails, the PageFactory falls back to searching for an element by the value of its “name” attribute.

The above is not the case for C# – a NoSuchElementException is thrown. The PageFactory implentation for C# only searches for elements using the ID and does not locate the elements using the NAME property, unless How = How.Name is explicitly specified.

class LoginPage{
    private IWebDriver driver;     [FindsBy]
    private IWebElement userName;     public LoginPage(IWebDriver driver) { this.driver = driver; }     public void Do(string UserName, string Password)
    {
        // userName is the NAME property, not ID
        // element will not be located
        // will throw a NoSuchElementException
        userName.SendKeys(UserName); 
    }} class Program{
    static void Main()
    {
        IWebDriver driver = new FirefoxDriver();
        driver.Navigate().GoToUrl("http://newtours.demoaut.com"); 
        LoginPage Login = new LoginPage(driver);
        PageFactory.InitElements(driver, Login);
        Login.Do("theUserName", "thePassword"); 
        driver.Quit();
    }}

The 3rd discrepancy I found was in the initial part of the same document and noticed the same behavior when testing with Eclipse. The Java implementation can locate the element even without the FindsBy attribute – this isn’t the case for C#. View this image that shows this feature with Java. The below code fails to work for Gmail page for Passwd textBox since the [FindsBy] attribute is not specified.

class GmailLoginPage{
    private IWebDriver driver;     [FindsBy]
    private IWebElement Email;     // element will not initialize because [FindsBy] attribute is missing
    private IWebElement Passwd;     public GmailLoginPage(IWebDriver driver) { this.driver = driver; }     public void Do(string UserName, string Password)
    {
        Email.SendKeys(UserName);         // fail here - NullReferenceException
        Passwd.SendKeys(Password);
    }} class Program{
    static void Main()
    {
        IWebDriver driver = new FirefoxDriver();
        driver.Navigate().GoToUrl("http://gmail.com"); 
        GmailLoginPage GmailLogin = new GmailLoginPage(driver);
        PageFactory.InitElements(driver, GmailLogin);
        GmailLogin.Do("theUserName", "thePassword"); 
        driver.Quit();
    }}


本文转载自:http://relevantcodes.com/pageobjects-and-pagefactory-design-patterns-in-selenium/

共有 人打赏支持
红焖鲤鱼
粉丝 113
博文 40
码字总数 29610
作品 0
浦东
QA/测试工程师
webdirver中pagefactory和pageObjects的区别

http://relevantcodes.com/pageobjects-and-pagefactory-design-patterns-in-selenium/

jeffsui
2014/03/14
0
0
Android UI自动化测试框架--zinc30

Zinc30是一个强大的Android UI自动化测试框架,支持建立健壮、可维护的黑盒测试用例。RD或者QA能够基于场景设计功能级和系统级测试。Zinc30符合Webdriver的 API规范,以更好地面向对象编程的...

keenz
2012/12/03
2.6K
0
WebDriver对象管理之PageObject与PageFactory对比

在之前的自动化框架搭建文章中有提到过对象管理(或元素管理),而WebDriver给我们提供了两种对象管理思路PageObject与PageFactory,今天我就对比下两种方式的异同~ 关于PageObject 将一个页...

测试开发栈
2017/06/08
0
0
Top 15 UI Test Automation Best Practices You Should Follow

In the past several years, I have heard many engineers from various projects complain about the stability and the reliability of UI automation tests. But are they really so unst......

Yuri Bushnev
2017/12/14
0
0
Android UI测试框架zinc30

Zinc30是一个强大的Android UI自动化测试框架,支持建立健壮、可维护的黑盒测试用例。RD或者QA能够基于场景设计功能级和系统级测试。Zinc30符合Webdriver的 API规范,以更好地面向对象编程的...

keenz
2012/12/03
1K
2

没有更多内容

加载失败,请刷新页面

加载更多

#mysql50# not Exist

data 目录有中文名称,可能是复制的"复件...."

少年不搬砖老大徒伤悲
19分钟前
1
0
在yii2中,让你action参数支持POST数据的小方法

我们先来看一段代码 class RaController extends Controller { public $enableCsrfValidation = false; public function actionSay($username = '',$city = ''){ echo "{$......

阿北2017
28分钟前
2
0
macOS 10.14 Mojave

在近日发布的 macOS 10.14 Mojave 操作系统中,有人发现它(在某些机型中)悄然禁用了先前的「次像素平滑渲染」选项,导致部分(尤其是使用非高分辨率显示器)用户觉得字体比先前的更细更难看...

火力全開
32分钟前
2
0
 实现分布式锁的正确姿势

实现分布式锁的正确姿势 原理分析 最近看到好多博主都在推分布式锁,实现方式很多,基于db、redis、zookeeper。zookeeper方式实现起来比较繁琐,这里我们就谈谈基于redis实现分布式锁的正确实...

DemonsI
37分钟前
2
0
微信小程序 、支付宝小程序、百度小程序和钉钉e应用的汇总

2018年9月25日 百度小程序开放企业申请 微信小程序:现在微信小游戏都热火朝天了,而且已经有了云开发,包括提供的能力、文档、社区等都相对完善,也有相关的变现方式; 支付宝小程序:https...

to_be_better
39分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部