UI 自动化的 PageObject 设计模式

news2024/11/26 12:21:45

目录

前言:

什么是 PageObject 模型?

为什么使用 PageObject 模型?

PO 模式优点

PageObject 实践


前言:

UI 自动化是一种软件测试方法,它主要用于检查应用程序的用户界面是否符合预期。PageObject 是 UI 自动化中的一个重要概念,它是一种将页面元素映射到 Python 对象的设计模式。

PageObject 模式就是对 HTML 页面以及元素细节的封装,并对外提供应用级别的 API,使你摆脱与 HTML 的纠缠。

什么是 PageObject 模型?

PageObject 模型是一种设计模式,其核心是减少代码重复(最小化代码更新/维护用例)以降低用例开发的工作量。利用 PageObject 模型,为每个网页创建 Page 类,测试场景中用的定位器/元素存储在单独的类文件中,并且测试用例在不同的文件中,使代码更加模块化。由于元素定位器和测试脚本是分开存储的,因此对 Web UI 元素的任何更改只需要在测试场景代码中进行更改即可。

基于 PageObject 模型的实现包含以下两点:

Page 类——将页面封装成 Page 类,页面元素为 Page 类的成员元素,页面功能放在 Page 类方法里。

测试类——针对这个 Page 类定义一个测试类,在测试类调用 Page 类的各个类方法完成测试。它使用 Page 类中的页面方法/方法与页面的 UI 元素进行交互。如果网页的 UI 有变化,只需要更新 Page 类,测试类无需改动。

为什么使用 PageObject 模型?

随着项目新需求的不断迭代,开发代码和测试代码的复杂性增加。因此,开发自动化测试代码时必须遵循正确的项目结构。否则,代码可能会变得难以维护。

Web 由各种 WebElement(例如,菜单项、文本框、复选框、单选按钮等)的不同网页组成。测试用例与这些元素交互,如果 Selenium 定位器没有以正确的方式管理,代码的复杂性将成倍增加。

测试代码的重复或定位器的重复使用会降低代码的可读性,从而导致代码维护的开销成本增加。例如,测试电子商务网站的登录功能,我们使用 Selenium 进行自动化测试,测试代码可以与网页的底层 UI 或定位器进行交互。如果修改了 UI 或该页面上元素的路径发生了变化,会发生什么情况?自动化测试用例将失败,因为该用例执行的过程在网页上找不到依赖的页面元素。如果你对所有网页采用相同的测试开发方法。在这种情况下,测试者必须花费大量精力来即时更新分散在不同页面中的定位器。

PO 模式优点

PageObject 模型的优点
现在大家已经了解了 PageObject 设计模式的基础知识,让我们来看看使用该设计模式的一些优点:

提高可重用性——不同 POM 类中的 PageObject 方法可以在不同的测试用例/测试套件中重用。因此,由于页面方法的可重用性增加,整体代码量将大大减少。

提升可维护性——由于测试场景和定位器是分开存储的,它使代码更清晰,并且在维护测试代码上花费的精力更少。

降低 UI 更改对用例造成的影响——即使 UI 中经常发生更改,也只需要在对象存储库(存储定位器)中进行更改,对测试场景几乎没有影响。

便与多个测试框架集成——由于测试实现与 PageObject 的存储库分离,我们可以将相同的存储库与不同的测试框架一起使用。例如,Test Case-1 可以使用 Robot 框架,Tese Case - 2 可以使用 pytest 框架等,单个测试套件可以包含使用不同测试框架实现的测试用例。

PageObject 实践

首先我们先看一个反例,一个不使用 PageObject 模式的自动化测试示例(测试用户登录场景):

/***
* Tests login feature
*/
public class Login {

    public void testLogin() {
        // fill login data on sign-in page
        driver.findElement(By.name("user_name")).sendKeys("userName");
        driver.findElement(By.name("password")).sendKeys("my supersecret password");
        driver.findElement(By.name("sign-in")).click();

        // verify h1 tag is "Hello userName" after login
        driver.findElement(By.tagName("h1")).isDisplayed();
        assertThat(driver.findElement(By.tagName("h1")).getText(), is("Hello userName"));
    }
}

这种写法有两个问题:

测试用例和 AUT 的定位器没有分离,两者耦合在一起。如果 AUT 的 UI 更改布局或登录的输入和处理方式,则用例本身必须更改。

如果多个页面都需要登录,则定位器将分布在多个测试用例中。

使用 PageObject 模式,测试方法(登录)写法如下:

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;

/**
 * Page Object encapsulates the Sign-in page.
 */
public class SignInPage {
  protected WebDriver driver;

  // <input name="user_name" type="text" value="">
  private By usernameBy = By.name("user_name");
  // <input name="password" type="password" value="">
  private By passwordBy = By.name("password");
  // <input name="sign_in" type="submit" value="SignIn">
  private By signinBy = By.name("sign_in");

  public SignInPage(WebDriver driver){
    this.driver = driver;
  }

  /**
    * Login as valid user
    *
    * @param userName
    * @param password
    * @return HomePage object
    */
  public HomePage loginValidUser(String userName, String password) {
    driver.findElement(usernameBy).sendKeys(userName);
    driver.findElement(passwordBy).sendKeys(password);
    driver.findElement(signinBy).click();
    return new HomePage(driver);
  }
}

用户登录以后的元素定位(用于断言)方法写法如下:

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;

/**
 * Page Object encapsulates the Home Page
 */
public class HomePage {
  protected WebDriver driver;

  // <h1>Hello userName</h1>
  private By messageBy = By.tagName("h1");

  public HomePage(WebDriver driver){
    this.driver = driver;
    if (!driver.getTitle().equals("Home Page of logged in user")) {
      throw new IllegalStateException("This is not Home Page of logged in user," +
            " current page is: " + driver.getCurrentUrl());
    }
  }

  /**
    * Get message (h1 tag)
    *
    * @return String message text
    */
  public String getMessageText() {
    return driver.findElement(messageBy).getText();
  }

  public HomePage manageProfile() {
    // Page encapsulation to manage profile functionality
    return new HomePage(driver);
  }
  /* More methods offering the services represented by Home Page
  of Logged User. These methods in turn might return more Page Objects
  for example click on Compose mail button could return ComposeMail class object */
}

登录测试用例使用上述两个 PageObject,如下所示。

/***
 * Tests login feature
 */
public class TestLogin {

  @Test
  public void testLogin() {
    SignInPage signInPage = new SignInPage(driver);
    /// login  
    HomePage homePage = signInPage.loginValidUser("userName", "password");
     // assert login result
    assertThat(homePage.getMessageText(), is("Hello userName"));
  }

}

注意事项

从上述例子中,可以看出 PageObject 的设计方式有很大的灵活性,这里也总结一下使用 PageObject 开发用例的注意事项:

PageObject 本身不进行断言。断言是测试用例的一部分,应该始终包含在测试代码中,即与测试内容相关的代码不应包含在 PageObject 中。

public void testMessagesAreReadOrUnread() {
    Inbox inbox = new Inbox(driver);
    inbox.assertMessageWithSubjectIsUnread("I like cheese");
    inbox.assertMessageWithSubjectIsNotUnread("I'm not fond of tofu");
}

应该重写为:

public void testMessagesAreReadOrUnread() {
    Inbox inbox = new Inbox(driver);
    assertTrue(inbox.isMessageWithSubjectIsUnread("I like cheese"));
    assertFalse(inbox.isMessageWithSubjectIsUnread("I'm not fond of tofu"));
}

单一的验证可以包含在 PageObject 内,即验证页面以及页面上的关键元素是否正确加载,且此验证应在实例化 PageObject 时完成。在上面的示例中, HomePage 构造函数检查预期页面是否加载完毕以执行测试代码。

附:以 PageObject 模式开发的完整的登录场景代码

public class LoginPage {
    private final WebDriver driver;

    public LoginPage(WebDriver driver) {
        this.driver = driver;

        // Check that we're on the right page.
        if (!"Login".equals(driver.getTitle())) {
            // Alternatively, we could navigate to the login page, perhaps logging out first
            throw new IllegalStateException("This is not the login page");
        }
    }

    // The login page contains several HTML elements that will be represented as WebElements.
    // The locators for these elements should only be defined once.
        By usernameLocator = By.id("username");
        By passwordLocator = By.id("passwd");
        By loginButtonLocator = By.id("login");

    // The login page allows the user to type their username into the username field
    public LoginPage typeUsername(String username) {
        // This is the only place that "knows" how to enter a username
        driver.findElement(usernameLocator).sendKeys(username);

        // Return the current page object as this action doesn't navigate to a page represented by another PageObject
        return this;    
    }

    // The login page allows the user to type their password into the password field
    public LoginPage typePassword(String password) {
        // This is the only place that "knows" how to enter a password
        driver.findElement(passwordLocator).sendKeys(password);

        // Return the current page object as this action doesn't navigate to a page represented by another PageObject
        return this;    
    }

    // The login page allows the user to submit the login form
    public HomePage submitLogin() {
        // This is the only place that submits the login form and expects the destination to be the home page.
        // A seperate method should be created for the instance of clicking login whilst expecting a login failure. 
        driver.findElement(loginButtonLocator).submit();

        // Return a new page object representing the destination. Should the login page ever
        // go somewhere else (for example, a legal disclaimer) then changing the method signature
        // for this method will mean that all tests that rely on this behaviour won't compile.
        return new HomePage(driver);    
    }

    // The login page allows the user to submit the login form knowing that an invalid username and / or password were entered
    public LoginPage submitLoginExpectingFailure() {
        // This is the only place that submits the login form and expects the destination to be the login page due to login failure.
        driver.findElement(loginButtonLocator).submit();

        // Return a new page object representing the destination. Should the user ever be navigated to the home page after submiting a login with credentials 
        // expected to fail login, the script will fail when it attempts to instantiate the LoginPage PageObject.
        return new LoginPage(driver);   
    }

    // Conceptually, the login page offers the user the service of being able to "log into"
    // the application using a user name and password. 
    public HomePage loginAs(String username, String password) {
        // The PageObject methods that enter username, password & submit login have already defined and should not be repeated here.
        typeUsername(username);
        typePassword(password);
        return submitLogin();
    }
}

  作为一位过来人也是希望大家少走一些弯路

在这里我给大家分享一些自动化测试前进之路的必须品,希望能对你带来帮助。

(软件测试相关资料,自动化测试相关资料,技术问题答疑等等)

相信能使你更好的进步!

点击下方小卡片

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/784861.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AI大模型时代下运维开发探索第一篇:ReAct工程初探

引子 人工智能大模型的出现&#xff0c;已渐渐地影响了我们的日常生活和工作方式。生活中无处不在的AI&#xff0c;使我们的生活变得更加智能和便捷。工作中&#xff0c;AI大模型的高效和精准&#xff0c;极大地提升了我们解决问题的效率。 是的&#xff0c;我们不能忽视AI大…

Delphi 开发者,显示图片请忘掉VCL中的 TImage 吧

目录 序言 使用TImageCollection和TVirtualImageList组件支持高分辨率图像 一、总览 二、使用图像收集组件TImageCollection 2.1 图像收集组件编辑器 2.2 将现有 TImageList 载入 TImageCollection 三、使用Virtual ImageList 组件 3.1 Virtual ImageList Component 编辑…

【数据结构】| 王道考研——树的前世今生

目录 一. &#x1f981; 前言二. &#x1f981; 各种树的知识点1. 树1.1 概念1.2 属性1.3 常考性质1.4 树转换成二叉树1.5 森林转换为二叉树1.6 二叉树转换为森林1.7 树的遍历1.8 森林的遍历 2. 二叉树2.1满二叉树2.2 完全二叉树2.3二叉排序树2.4 平衡二叉树2.5 二叉树常考性质…

IDEA使用lombok实体类加上@Data注解后无法找到get和set方法

文章目录 一、问题原因二、解决方法1.File→Settings2.Plugins→搜索"lombok"→Install3.Restart IDE&#xff08;重启IDEA&#xff09; 一、问题原因 IDEA没有安装lombok插件 二、解决方法 1.File→Settings 2.Plugins→搜索"lombok"→Install 3.Restart…

北斗定位导航系统,北斗模块应用领域发展概况_北斗二号模块,北斗三号模块

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、北斗系统概述1.1 空间段1.2 地面控制段1.3 用户段1.4 坐标系统1.5 时间系统 二、北斗系统定位导航授时服务2.1 服务概述2.2 服务区2.3 北斗信号频段2.4 北斗…

服务器中了Locked勒索病毒怎么解决,勒索病毒解密恢复方式与防护措施

服务器是企业重要数据存储和处理的关键设备&#xff0c;然而&#xff0c;众所周知&#xff0c;服务器系统并非完全免受网络攻击的。其中一种常见的威胁是勒索病毒&#xff0c;其中一种恶名昭彰的变种是Locked勒索病毒。Locked勒索病毒采用了对称AES与非对称RSA的加密形式&#…

【C++入门到精通】C++入门 —— 引用、内联函数

目录 一、引用 1.引用的概念 2.引用的特性 3.常引用 4.引用的使用场景 ⭕做参数 ⭕做返回值 5.传值、传引用效率比较 值和引用的作为返回值类型的性能比较 6.引用和指针的区别 引用和指针的不同点 二、内联函数 1.内联函数的概念 2.内联函数的特性 3.宏与内联函数 …

RocketMQ教程-(5)-功能特性-顺序消息

顺序消息为 Apache RocketMQ 中的高级特性消息&#xff0c;本文为您介绍顺序消息的应用场景、功能原理、使用限制、使用方法和使用建议。 应用场景​ 在有序事件处理、撮合交易、数据实时增量同步等场景下&#xff0c;异构系统间需要维持强一致的状态同步&#xff0c;上游的事…

13.4.2 【Linux】sudo

相对于 su 需要了解新切换的使用者密码 &#xff08;常常是需要 root 的密码&#xff09;&#xff0c; sudo 的执行则仅需要自己的密码即可。sudo 可以让你以其他用户的身份执行指令 &#xff08;通常是使用 root 的身份来执行指令&#xff09;&#xff0c;因此并非所有人都能够…

Spring 多数据源方法级别注解实现

Spring框架提供了多种数据源管理方式&#xff0c;其中多数据源管理是其中之一。多数据源管理允许应用程序使用多个数据源&#xff0c;而不是只使用一个数据源&#xff0c;从而提高了应用程序的灵活性和可靠性。 多数据源管理的主要目的是让应用程序能够在不同的数据库之间切换&…

SpringCache 框架使用以及序列化和缓存过期时间问题的解决

目录 为什么使用Spring Cache 如何使用Spring Cache 1 加依赖 2 开启缓存 3 加缓存注解 序列化以及过期时间等问题 解决方案&#xff1a;自定义序列化方式 1.自定义序列化方式并设置白名单 2.配置并设置缓存的过期时间 为什么使用Spring Cache 缓存有诸多的好处&#x…

YOLOv5(v7.0)网络修改实践二:把单分支head改为YOLOX的双分支解耦合head(DecoupleHead)

前面研究了一下YOLOX的网络结构&#xff0c;在YOLOv5(tag7.0)集成了yolox的骨干网络&#xff0c;现在继续下一步集成YOLOX的head模块。YOLOX的head模块是双分支解耦合网络&#xff0c;把目标置信度的预测和目标的位置预测分成两条支路&#xff0c;并验证双分支解耦合头性能要优…

为什么视频画质会变差,如何提升视频画质清晰度。

在数字时代&#xff0c;视频已经成为我们生活中不可或缺的一部分。然而&#xff0c;随着视频的传输和处理过程中的多次压缩&#xff0c;画质损失逐渐凸显&#xff0c;影响了我们对影像的真实感受。为了让视频画质更加清晰、逼真&#xff0c;我们需要采取一些措施来保护和修复视…

设计模式大白话——观察者模式

文章目录 一、概述二、示例三、模式定义四、其他 一、概述 ​ 与其叫他观察者模式&#xff0c;我更愿意叫他叫 订阅-发布模式 &#xff0c;这种模式在我们生活中非常常见&#xff0c;比如&#xff1a;追番了某个电视剧&#xff0c;当电视剧有更新的时候会第一时间通知你。当你…

Zia和ChatGPT如何协同工作?

有没有集成ChatGPT的CRM系统推荐&#xff1f;Zoho CRM已经正式与ChatGPT集成。下面我们将从使用场景、使用价值和使用范围等方面切入讲述CRMAI的应用和作用。 Zia和ChatGPT如何协同工作&#xff1f; Zia和ChatGPT是不同的人工智能模型&#xff0c;在CRM中呈现出共生的关系。 …

荧光信号采集的2种方法

本文介绍荧光信号采集的2种方法。 荧光信号采集是生化类医疗设备&#xff08;PCR&#xff0c;荧光免疫分析仪&#xff09;常用的功能&#xff0c;针对不同类型的激发光和发射光采用的荧光信号采集方法也不一样。 1.基本概念 1)发光原理 当待测物质受到外接某一波长的入射光…

生物信息学_玉泉路_课堂笔记_02 第二章 序列比对和序列数据库搜索

&#x1f345; 课程&#xff1a;生物信息学_玉泉路_课堂笔记 中科院_2022秋季课 第一学期 &#x1f345; 个人笔记使用 &#x1f345; 2023/7/5 一、上周回顾 二、生物信息学的展望 —— 精准医学的发展 生物信息学的开拓者 三、双序列比对的常用算法 序列比对的需求 使用 b…

基于包围框回归的目标检测网络原理及Tensorflow实现

对象检测是对图像内的对象进行分类和定位。 换句话说&#xff0c;它是图像分类和对象定位的结合。 构建用于图像分类的机器学习模型更简单&#xff0c;我在我的一篇文章中对此进行了描述。 然而&#xff0c;图像分类器无法准确判断对象在图像内的位置。 为了实现这一目标&#…

opencv对相机进行畸变矫正,及从矫正后的图像坐标反求原来的对应坐标

1.背景 目前有个项目&#xff0c;需要用到热成像相机。但是这个热成像相机它的畸变比较厉害&#xff0c;因此需要用标定板进行标定&#xff0c;从而消除镜头畸变。 同时需要实现用户用鼠标点击矫正后的画面后&#xff0c;显示用户点击位置的像素所代表的温度。 2.难点 消除镜…

1-1 AUTOSAR是什么?

目录 一、AUTOSAR组织 二、AUTOSAR的版本 三、AUTOSAR的理念 四、AUTOSAR的动机 五、AUTOSAR目标 六、AUTOSAR的优势 6.1 商业优势 6.2 技术优势 一、AUTOSAR组织 AUTOSAR&#xff08;Automotive Open System Architecture&#xff09;是一个全球性的汽车行业标准化组织…