51Testing软件测试论坛

标题: 如何在WEB自动化中进行Page Object 模型? [打印本页]

作者: 海鸥一飞    时间: 2022-10-8 13:37
标题: 如何在WEB自动化中进行Page Object 模型?
本帖最后由 海鸥一飞 于 2022-10-8 13:38 编辑

Page Object 模型概述
    在针对一个WEB页面编写自动化测试用例时,需要引用页面中的元素(数据)才能进行操作(动作)并显示出页面内容。如果编写的测试用例是直接针对WEB页面元素进行操作,则无法应对经常发生变化的WEB页面,增加日后自动化代码的维护成本。而Page Object模型就是针对WEB页面和元素细节的封装,并对外提供应用级别的API,从而摆脱对WEB页面的高耦合情况。示意图如下所示:
[attach]143584[/attach]
针对以上示例,可以大概总结出大概做法,如下所示:

2 定义
    Page Object模型(简称为PO模式)是一种设计模式,其核心是分层,实现松耦合,从而实现代码复用和其易维护性。利用PO模型,为每个网页建立两个类:
    将每个页面封装为Page类,页面元素为Page类成员变量页面功能为Page类方法里面
    针对Page类定义的测试类,在测试类中调用Page类中方法完成测试。其使用Page类中的方法与页面UI元素进行交互操作。若UI发生变化,仅需要更新Page类,测试类无需要更改。
10.3 为什么使用Page Object模式
    WEB由各种WEB元素(文本框、复选框、多选/单选按钮等)组成。测试代码与这些元素进行交互,如果不能正确管理定位器,则代码的复杂性将成倍增加。当测试代码和定位器的重复使用,将降低代码的可读性,从而进一步加大测试代码的维护成本。 随着项目和需求的不断变化,开发和测试代码的复杂性会不断增加,维护性也随之增加。因此,需要一种方法来解决这种问题,所以我们需要使用PO来尝试解决这一类问题。
4 Page Object模型优点
    主要优点如下所示:
    不同PO类中的Pabe Object方法可以在不同的测试用例中复用,极大提高代码的复用性。
    因测试场景和定位器是代码分开,使代码更加清晰,极大提高代码的可维护性。
    尽管UI经常发生变更,也仅需要修改少量代码来应对更改,从而减少其带来的影响。
5 Page Object示例1 演示环境搭建
    我们以官方提供的示例为演示,操作步骤如下所示:
  1. <font size="3">npm install minimist morgan body-parser express-session express hbs --save-dev
  2. npm start server.js
  3. </font>
复制代码

[attach]143585[/attach]
默认正确的用户名和密码,在server.js中,可以自行修改,如下所示:
[attach]143586[/attach]
2 演示代码
    本代码仅仅是演示在Cypress中的Page Object模式(注意与Selenium的区别),主要示例代码如下所示:

  1. <font size="3">{
  2.    "loginPage":{
  3.        "username":"input[name=\"username\"]",
  4.        "passwd":"input[name=\"password\"]",
  5.        "submit":"button[type=\"submit\"]",
  6.        "loginFailedPrompt":".error"
  7.    }
  8. }
  9. </font>
复制代码
  1. <font size="3">/// <reference types="cypress" />

  2. import LoginPageLocator from "./loginPageLoctor.json"

  3. export default class LoginPage{
  4.     constructor(visitUrl){
  5.        this.url=visitUrl;
  6.     }

  7.     get username(){
  8.         return cy.get(LoginPageLocator.loginPage.username);
  9.     }

  10.     get passwd(){
  11.         return cy.get(LoginPageLocator.loginPage.passwd);
  12.     }

  13.     get submit(){
  14.         return cy.get(LoginPageLocator.loginPage.submit);
  15.     }

  16.     get errorPrompt(){
  17.         return cy.get(LoginPageLocator.loginPage.loginFailedPrompt);
  18.     }

  19.     get successUrl(){
  20.         return cy.url();
  21.     }

  22.     visit(){
  23.        cy.visit(this.url);
  24.     }

  25.     login(name,pwd) {
  26.         if ( name !="" && pwd !=""){
  27.             this.username.type(name);
  28.         }

  29.         if(pwd!=""){
  30.             this.passwd.type(pwd);
  31.         }

  32.         this.submit.click();
  33.     }

  34. }
  35. </font>
复制代码
  1. <font size="3">{

  2.     "success": [
  3.         {
  4.             "caseTitle": "正确的用户名和密码,登录成功",
  5.             "user": "jane.lane",
  6.             "pwd": "password123",
  7.             "checkpoint": "/dashboard"
  8.         }
  9.     ],

  10.     "failed": [
  11.         {
  12.             "caseTitle": "错误的用户名和正确的密码,登录失败",
  13.             "user": "Surpass",
  14.             "pwd": "password123",
  15.             "checkpoint": "Username and/or password is incorrect"
  16.         },
  17.         {
  18.             "caseTitle": "正确的用户名和错误的密码,登录失败",
  19.             "user": "jane.lane",
  20.             "pwd": "Surpass",
  21.             "checkpoint": "Username and/or password is incorrect"
  22.         },
  23.         {
  24.             "caseTitle": "错误的用户名和错误的密码,登录失败",
  25.             "user": "Surpass",
  26.             "pwd": "Surpass",
  27.             "checkpoint": "Username and/or password is incorrect"
  28.         }
  29.     ]

  30. }
  31. </font>
复制代码

  1. <font size="3">/// <reference types="cypress" />

  2. import LoginPage from "./loginPage"
  3. import UserData from "./loginData.json"

  4. describe('登录测试', () => {
  5.     let baseUrl = "http://localhost:7077/login";
  6.     let login = new LoginPage(baseUrl);
  7.     beforeEach(() => {
  8.         login.visit(baseUrl);
  9.     });

  10.     UserData.success.forEach((item)=>{
  11.        it(item.caseTitle, () => {
  12.            login.login(item.user,item.pwd);
  13.            login.successUrl.should("contain",item.checkpoint)
  14.        });
  15.     });

  16.     UserData.failed.forEach((item)=>{
  17.        it(item.caseTitle, () => {
  18.         login.login(item.user,item.pwd);
  19.         login.errorPrompt.should("contain",item.checkpoint)
  20.        });
  21.     });
  22. });
  23. </font>
复制代码
最终的运行结果如下所示:
[attach]143587[/attach]






欢迎光临 51Testing软件测试论坛 (http://bbs.51testing.com/) Powered by Discuz! X3.2