Selenium 中的页面对象模型与工厂

什么是页面对象模型?

页面对象模型 (POM) 是一种设计模式,在测试自动化中广泛使用,它为 web UI 元素创建对象库。该模型的优点是减少了代码重复并提高了测试维护性。

在此模型下,应用程序中的每个网页都应该有一个相应的页面类。此页面类将识别该网页的 WebElements,并且还包含对这些 WebElements 执行操作的页面方法。这些方法的名称应根据它们执行的任务来命名,即,如果加载程序正在等待支付网关出现,则 POM 方法名称可以是 waitForPaymentScreenDisplay()。

Page Object Model

为什么使用页面对象模型?

在 Selenium WebDriver 中启动 UI 自动化并非难事。你只需找到元素,然后对其执行操作即可。

考虑下面这个简单的脚本来登录网站

Page Object Model

正如你所观察到的,我们所做的只是查找元素并为这些元素填充值。

这是一个小脚本。脚本维护看起来很简单。但随着时间的推移,测试套件会增长。随着代码中添加的行越来越多,事情变得越来越困难。

脚本维护的主要问题是,如果 10 个不同的脚本使用同一个页面元素,那么该元素的任何更改都需要更改所有 10 个脚本。这既耗时又容易出错。

一种更好的脚本维护方法是创建一个单独的类文件,该文件将查找 web 元素、填充它们或验证它们。该类可以在所有使用该元素的脚本中重复使用。将来,如果 web 元素发生变化,我们只需要更改 1 个类文件,而不是 10 个不同的脚本。

这种方法在 Selenium 中称为页面对象模型。它有助于使代码更具可读性、可维护性和可重用性。

 Page Object Model

POM 的优势

  1. 页面对象设计模式指出 UI 中的操作和流程应与验证分离。这个概念使我们的代码更清晰易懂。
  2. 第二个好处是对象存储库独立于测试用例,因此我们可以将相同的对象存储库用于不同工具的不同目的。例如,我们可以将 Selenium 中的页面对象模型与 TestNG/JUnit 集成用于功能测试,同时与 JBehave/Cucumber 集成用于验收测试。
  3. 由于 POM 类中可重用的页面方法,代码变得更少且更优化。
  4. 方法获得了更逼真的名称,可以轻松地与 UI 中发生的操作进行映射。例如,如果单击按钮后我们进入主页,方法名称将类似于“gotoHomePage()”。

如何实现 POM?

简单的 POM

这是页面对象模型框架的基本结构,其中 AUT 的所有 Web 元素以及对这些 Web 元素进行操作的方法都维护在一个类文件中。验证等任务应作为测试方法的一部分单独进行。

Implement POM

完整示例

测试用例:访问 Guru99 演示站点。

步骤 1) 访问 Guru99 演示站点

Implement POM

步骤 2) 在主页中检查文本“Guru99 Bank”是否存在

Implement POM

步骤 3) 登录应用程序

Implement POM

步骤 4) 验证主页是否包含文本“经理 ID:demo”

Implement POM

这里我们正在处理 2 个页面

  1. 登录页面
  2. 主页(登录后显示)

因此,我们在 Selenium 中创建 2 个 POM 类

Guru99 登录页面 POM

package pages;

import org.openqa.selenium.By;

import org.openqa.selenium.WebDriver;

public class Guru99Login {

    WebDriver driver;

    By user99GuruName = By.name("uid");

    By password99Guru = By.name("password");

    By titleText =By.className("barone");

    By login = By.name("btnLogin");

    public Guru99Login(WebDriver driver){

        this.driver = driver;

    }

    //Set user name in textbox

    public void setUserName(String strUserName){

        driver.findElement(user99GuruName).sendKeys(strUserName);

    }

    //Set password in password textbox

    public void setPassword(String strPassword){

         driver.findElement(password99Guru).sendKeys(strPassword);

    }

    //Click on login button

    public void clickLogin(){

            driver.findElement(login).click();

    }

    //Get the title of Login Page

    public String getLoginTitle(){

     return    driver.findElement(titleText).getText();

    }

    /**

     * This POM method will be exposed in test case to login in the application

     * @param strUserName

     * @param strPasword

     * @return

     */

    public void loginToGuru99(String strUserName,String strPasword){

        //Fill user name

        this.setUserName(strUserName);

        //Fill password

        this.setPassword(strPasword);

        //Click Login button

        this.clickLogin();        
    }

}

Selenium 中的 Guru99 主页 POM

package pages;

import org.openqa.selenium.By;

import org.openqa.selenium.WebDriver;

public class Guru99HomePage {

    WebDriver driver;

    By homePageUserName = By.xpath("//table//tr[@class='heading3']");

    

    public Guru99HomePage(WebDriver driver){

        this.driver = driver;

    }

    //Get the User name from Home Page

        public String getHomePageDashboardUserName(){

         return    driver.findElement(homePageUserName).getText();

        }

}

Selenium 测试用例中的 Guru99 简单 POM

package test;

import java.util.concurrent.TimeUnit;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.firefox.FirefoxDriver;

import org.testng.Assert;

import org.testng.annotations.BeforeTest;

import org.testng.annotations.Test;

import pages.Guru99HomePage;

import pages.Guru99Login;

public class Test99GuruLogin {

    String driverPath = "C:\\geckodriver.exe";
    
    WebDriver driver;

    Guru99Login objLogin;

    Guru99HomePage objHomePage;

    @BeforeTest

    public void setup(){

	System.setProperty("webdriver.gecko.driver", driverPath);
        
        driver = new FirefoxDriver();

        driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);

        driver.get("https://demo.guru99.com/V4/");

    }

    /**

     * This test case will login in https://demo.guru99.com/V4/

     * Verify login page title as guru99 bank

     * Login to application

     * Verify the home page using Dashboard message

     */

    @Test(priority=0)

    public void test_Home_Page_Appear_Correct(){

        //Create Login Page object

    objLogin = new Guru99Login(driver);

    //Verify login page title

    String loginPageTitle = objLogin.getLoginTitle();

    Assert.assertTrue(loginPageTitle.toLowerCase().contains("guru99 bank"));

    //login to application

    objLogin.loginToGuru99("mgr123", "mgr!23");

    // go the next page

    objHomePage = new Guru99HomePage(driver);

    //Verify home page

    Assert.assertTrue(objHomePage.getHomePageDashboardUserName().toLowerCase().contains("manger id : mgr123"));

    }

Selenium 中的 Page Factory 是什么?

Selenium 中的 Page Factory 是 Selenium WebDriver 的内置页面对象模型框架概念,但它非常优化。它用于初始化页面对象或实例化页面对象本身。它还用于在不使用“FindElement/s”的情况下初始化页面类元素。

在这里,我们同样遵循页面对象库和测试方法分离的概念。此外,借助 Selenium 中的 PageFactory 类,我们使用注解 @FindBy 来查找 WebElement。我们使用 initElements 方法来初始化 web 元素

Page Factory In Selenium

@FindBy 可以接受 tagName、partialLinkText、name、linkText、id、css、className、xpath 作为属性。

让我们使用 Page Factory 来看上面的同一个例子

使用 Page Factory 的 Guru99 登录页面

package PageFactory;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.support.FindBy;

import org.openqa.selenium.support.PageFactory;

public class Guru99Login {

    /**

     * All WebElements are identified by @FindBy annotation

     */

    WebDriver driver;

    @FindBy(name="uid")

    WebElement user99GuruName;

    @FindBy(name="password")

    WebElement password99Guru;    

    @FindBy(className="barone")

    WebElement titleText;

    @FindBy(name="btnLogin")

    WebElement login;

    public Guru99Login(WebDriver driver){

        this.driver = driver;

        //This initElements method will create all WebElements

        PageFactory.initElements(driver, this);

    }

    //Set user name in textbox

    public void setUserName(String strUserName){

        user99GuruName.sendKeys(strUserName);     
    }

    //Set password in password textbox

    public void setPassword(String strPassword){

    password99Guru.sendKeys(strPassword);

    }

    //Click on login button

    public void clickLogin(){

            login.click();

    }  

    //Get the title of Login Page

    public String getLoginTitle(){

     return    titleText.getText();

    }

    /**

     * This POM method will be exposed in test case to login in the application

     * @param strUserName

     * @param strPasword

     * @return

     */

    public void loginToGuru99(String strUserName,String strPasword){

        //Fill user name

        this.setUserName(strUserName);

        //Fill password

        this.setPassword(strPasword);

        //Click Login button

        this.clickLogin();           

    }

}

使用 Page Factory 的 Guru99 主页

package PageFactory;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.support.FindBy;

import org.openqa.selenium.support.PageFactory;

public class Guru99HomePage {

    WebDriver driver;

    @FindBy(xpath="//table//tr[@class='heading3']")

    WebElement homePageUserName;    

    public Guru99HomePage(WebDriver driver){

        this.driver = driver;

        //This initElements method will create all WebElements

        PageFactory.initElements(driver, this);

    }   

    //Get the User name from Home Page

        public String getHomePageDashboardUserName(){

         return    homePageUserName.getText();

        }

}

Guru99 使用 Page Factory 概念的测试用例

package test;

import java.util.concurrent.TimeUnit;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.firefox.FirefoxDriver;

import org.testng.Assert;

import org.testng.annotations.BeforeTest;

import org.testng.annotations.Test;

import PageFactory.Guru99HomePage;

import PageFactory.Guru99Login;

public class Test99GuruLoginWithPageFactory {

    String driverPath = "C:\\geckodriver.exe";
    
    WebDriver driver;

    Guru99Login objLogin;

    Guru99HomePage objHomePage; 

    @BeforeTest

    public void setup(){

        System.setProperty("webdriver.gecko.driver", driverPath);
        
        driver = new FirefoxDriver();

        driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);

        driver.get("https://demo.guru99.com/V4/");

    }

    /**

     * This test go to https://demo.guru99.com/V4/

     * Verify login page title as guru99 bank

     * Login to application

     * Verify the home page using Dashboard message

     */

    @Test(priority=0)

    public void test_Home_Page_Appear_Correct(){

        //Create Login Page object

    objLogin = new Guru99Login(driver);

    //Verify login page title

    String loginPageTitle = objLogin.getLoginTitle();

    Assert.assertTrue(loginPageTitle.toLowerCase().contains("guru99 bank"));

    //login to application

    objLogin.loginToGuru99("mgr123", "mgr!23");

    // go the next page

    objHomePage = new Guru99HomePage(driver);

    //Verify home page

    Assert.assertTrue(objHomePage.getHomePageDashboardUserName().toLowerCase().contains("manger id : mgr123"));

    }

}

完整的项目结构将如下图所示

Page Factory in Selenium

AjaxElementLocatorFactory

AjaxElementLocatorFactory 是 Selenium 中 PageFactory 的延迟加载概念。它仅在元素用于任何操作时才用于查找 web 元素。它为对象页面类中的 WebElements 分配超时。使用 Selenium 中 PageFactory 模式的主要优势之一是 AjaxElementLocatorFactory 类。

在这里,当对元素执行操作时,其可见性的等待仅从那一刻开始。如果在给定时间间隔内未找到元素,测试用例执行将抛出“NoSuchElementException”异常。

AjaxElement Locator Factory

摘要

  1. Selenium WebDriver 中的页面对象模型是一种对象存储库设计模式。
  2. Selenium 页面对象模型使我们的测试代码可维护、可重用。
  3. Page Factory 是一种优化方法,用于在页面对象模型框架概念中创建对象存储库。
  4. AjaxElementLocatorFactory 是 Page Factory 页面对象设计模式中的一个延迟加载概念,仅在 Web 元素用于任何操作时才识别它们。

下载本教程演示的 Selenium 项目文件