🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# 页面对象模型(POM)&页面工厂:Selenium WebDriver 教程 > 原文: [https://www.guru99.com/page-object-model-pom-page-factory-in-selenium-ultimate-guide.html](https://www.guru99.com/page-object-model-pom-page-factory-in-selenium-ultimate-guide.html) ## 什么是页面对象模型? **页面对象模型(POM)**是一种设计模式,广泛用于测试自动化中,可为 Web UI 元素创建**对象存储库**。 该模型的优点是它减少了代码重复并改善了测试维护。 在此模型下,对于应用程序中的每个网页,应有一个对应的**页面类。** 此类将标识该网页的 WebElement,并且还包含对这些 WebElement 执行操作的 **Page 方法**。 这些方法的名称应根据它们正在执行的任务来指定,即如果加载程序正在等待支付网关的出现,则 POM 方法名称可以是 waitForPaymentScreenDisplay()。 ![Page Object Model (POM) & Page Factory in Selenium: Complete Tutorial](https://img.kancloud.cn/47/34/4734345e16382220025c4d2103d5059f_553x254.png "Page Object Model (POM) & Page Factory in Selenium: Ultimate Guide") 在本教程中,您将学习- * [为什么选择页面对象模型?](#1) * [POM 的优点](#3) * [如何实现 POM?](#4) * [什么是页面工厂?](#5) * [具有页面工厂概念的 Guru99 TestCase](#6) * [AjaxElementLocatorFactory](#7) ## 为什么选择页面对象模型? 在 Selenium WebDriver 中启动 UI 自动化并不是一项艰巨的任务。 您只需要查找元素并对其执行操作即可。 考虑这个简单的脚本来登录网站 ![Page Object Model (POM) & Page Factory in Selenium: Complete Tutorial](https://img.kancloud.cn/bc/53/bc53f6a1bbcf0b9961553ae99152a82e_624x297.png "Page Object Model (POM) & Page Factory in Selenium: Ultimate Guide") 如您所见,我们所做的只是查找元素并为这些元素填充值。 这是一个小脚本。 脚本维护看起来很容易。 但是随着时间的推移,测试套件将不断增长。 随着在代码中添加越来越多的行,事情变得越来越棘手。 脚本维护的主要问题是,如果 10 个不同的脚本使用相同的 page 元素,但对该元素进行了任何更改,则需要更改所有 10 个脚本。 这既费时又容易出错。 一种更好的脚本维护方法是创建一个单独的类文件,该文件将查找 Web 元素,填充它们或对其进行验证。 可以使用该元素在所有脚本中重用该类。 将来,如果 web 元素发生更改,我们只需要在 1 个类文件而不是 10 个不同的脚本中进行更改。 这种方法称为**页面对象模型(POM)**。 它有助于使代码**更具可读性,可维护性**和**可重用。** ![Page Object Model (POM) & Page Factory in Selenium: Complete Tutorial](https://img.kancloud.cn/09/fa/09fa67a7490ba9fb5263abaf1f0530a5_464x294.png "Page Object Model (POM) & Page Factory in Selenium: Ultimate Guide") ## POM 的优点 1. 页面对象模式表示,UI 中的操作和流程应与验证分开。 这个概念使我们的代码更加简洁易懂。 2. 第二个好处是**对象库独立于测试用例**,因此我们可以使用不同的工具将同一对象库用于不同的目的。 例如,我们可以将 POM 与 TestNG / JUnit 集成以进行功能[测试](/software-testing.html),同时与 JBehave / Cucumber 集成以进行验收测试。 3. 由于 POM 类中的可重复使用的页面方法,因此代码变得更少并得到了优化。 4. **方法**获得了**更现实的名称**,可以通过 UI 中发生的操作轻松映射它们。 即,如果在点击按钮后我们进入首页,则方法名称将类似于“ gotoHomePage()”。 ## 如何实现 POM? 简单的 POM: 这是页面对象模型(POM)的基本结构,其中 **AUT** 的所有 Web 元素以及在这些 Web 元素上运行的方法都保存在类文件中。**验证**之类的任务 作为测试方法的一部分,应将**分开**。 ![Page Object Model (POM) & Page Factory in Selenium: Complete Tutorial](https://img.kancloud.cn/62/f3/62f3ced18542cfc50dea309d828056a6_524x236.png "Page Object Model (POM) & Page Factory in Selenium: Ultimate Guide") **完整示例** **TestCase:**转到 Guru99 演示站点。 <colgroup><col> <col></colgroup> | 步骤 1)前往 Guru99 示范网站 | ![Page Object Model (POM) & Page Factory in Selenium: Complete Tutorial](https://img.kancloud.cn/0f/ac/0fac926a0e377aa598eb8f188fd2d8ce_266x121.png "Page Object Model (POM) & Page Factory in Selenium: Ultimate Guide") | | 步骤 2)在首页中,检查文本**“ Guru99 Bank”** 存在 | ![Page Object Model (POM) & Page Factory in Selenium: Complete Tutorial](https://img.kancloud.cn/d6/ad/d6adc16b990c994685ec6046085688bb_329x202.png "Page Object Model (POM) & Page Factory in Selenium: Ultimate Guide") | | 步骤 3)登录到应用程序 | ![Page Object Model (POM) & Page Factory in Selenium: Complete Tutorial](https://img.kancloud.cn/ad/73/ad7381ddd01c64b2d5db9c4234b203d2_325x186.png "Page Object Model (POM) & Page Factory in Selenium: Ultimate Guide") | | 步骤 4)确认首页包含“ Manger ID:演示”文本 | ![Page Object Model (POM) & Page Factory in Selenium: Complete Tutorial](https://img.kancloud.cn/24/6e/246e9fdfaf13bcac4013c6b769b76e72_349x126.png "Page Object Model (POM) & Page Factory in Selenium: Ultimate Guide") | 这是我们正在处理的 2 页 1. 登录页面 2. 主页(登录后显示) 因此,我们创建了 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(); } } ``` **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(); } } ``` **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("http://demo.guru99.com/V4/"); } /** * This test case will login in http://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 是 Selenium WebDriver 的内置页面对象模型概念,但已进行了非常优化。 同样,在这里,我们遵循页面对象存储库和测试方法分离的概念。 另外,在 PageFactory 类的帮助下,我们使用批注 **@FindBy** 查找 WebElement。 我们使用 initElements 方法来初始化 Web 元素 ![Page Object Model (POM) & Page Factory in Selenium: Complete Tutorial](https://img.kancloud.cn/f1/ea/f1ea83930a36f553947b9e189d3b5c61_616x252.png "Page Object Model (POM) & Page Factory in Selenium: Ultimate Guide") **@FindBy** 可以接受 **tagName,partialLinkText,名称,linkText,id,css,className,xpath** 作为属性。 让我们看看使用 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(); } } ``` **具有页面工厂**的 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(); } } ``` ## 具有 Page Factory 概念的 Guru99 TestCase ``` 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("http://demo.guru99.com/V4/"); } /** * This test go to http://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 Object Model (POM) & Page Factory in Selenium: Complete Tutorial](https://img.kancloud.cn/81/ea/81ead40865fa239eb2d56f681902358e_530x278.png "Page Object Model (POM) & Page Factory in Selenium: Ultimate Guide") ## AjaxElementLocatorFactory 使用 Page Factory 模式的主要优点之一是 AjaxElementLocatorFactory 类。 它正在处理延迟加载概念,即在 AjaxElementLocatorFactory 的帮助下将 WebElement 的超时分配给 Object 页面类。 在此,当对元素执行操作时,等待其可见性仅从该时刻开始。 如果在给定的时间间隔内未找到该元素,则[测试用例](/test-case.html)执行将引发'NoSuchElementException'异常。 ![Page Object Model (POM) & Page Factory in Selenium: Complete Tutorial](https://img.kancloud.cn/d4/59/d4599fa7bead345fe025a422ce09916c_601x214.png "Page Object Model (POM) & Page Factory in Selenium: Ultimate Guide") ## 摘要 1. 页面对象模型是 Selenium WebDriver 中的对象存储库设计模式。 2. POM 创建了可维护,可重用的测试代码。 3. Page Factory 是在 POM 概念中创建对象存储库的一种优化方法。 4. AjaxElementLocatorFactory 是 Page Factory 模式中的惰性加载概念,用于仅在任何操作中使用 WebElement 时标识它们。 [在本教程中下载用于演示的 Selenium 项目文件](https://drive.google.com/uc?export=download&id=1RzLkWLBV3dpolQxcP5V2aJ5YmzJYJbTf)