PO 模式

PageObject(页面对象)模式是自动化测试中的一个最佳实践,相信很多小伙伴都知道的

PO 模式特征

  • 将每个页面(或者待测试对象)封装成一个(class),类里面包含了页面上所有元素及它们的操作方法(单步操作或功能集合)
  • 测试代码和被测页面代码解耦,使用 PO 模式后,当页面发生改变,无须改变测试代码,仅改页面代码


    接下来就讲解下 Cypress 下如何使用 PO 模式

    前期准备

    启动 Cypress 提供的演示项目

    cmd 窗口进入下面的文件夹
    Cypress系列(62)- 改造 PageObject 模式 - 图1

    执行下面的命令

  1. npm start

PO 模式代码

简单的 PageObject 模型栗子

待测试页面代码

在 C:\Users\user\Desktop\py\cypress-example-recipes\examples\logging-in__html-web-forms\cypress 文件夹下新建 pages 文件夹,并创建一个 login.js 待测试页面文件,代码如下

  1. // login.js
  2. export default class LoginPage {
  3. constructor() {
  4. this.userName = 'input[name=username]'
  5. this.password = 'input[name=password]'
  6. this.form = 'form'
  7. this.url = 'http://localhost:7077/login'
  8. }
  9. isTargetPage() {
  10. cy.visit('/login')
  11. cy.url().should('eq', this.url)
  12. }
  13. login(username, pwd) {
  14. cy.get(this.userName).type(username)
  15. cy.get(this.password).type(pwd)
  16. cy.get(this.form).submit()
  17. }
  18. }

测试用例文件

在 C:\Users\user\Desktop\py\cypress-example-recipes\examples\logging-in__html-web-forms\cypress\integration 文件夹下,创建一个 testLogin.js 测试用例文件,代码如下

  1. import LoginPage from "../pages/login"
  2. context('登录测试,PO 模式', function () {
  3. const username = 'jane.lane'
  4. const pwd = 'password123'
  5. it('登录成功', function () {
  6. // 创建 po 实例
  7. const loginInstance = new LoginPage()
  8. loginInstance.isTargetPage()
  9. loginInstance.login(username, pwd)
  10. cy.url().should('include', '/dashboard')
  11. });
  12. })

测试结果

Cypress系列(62)- 改造 PageObject 模式 - 图2

总结下

这样的 PageObject 模式代码只是把定位元素的元素定位表达式给剥离出来,并没有针对元素本身进行封装

针对元素本身进行封装的栗子

待测试页面代码

  1. // login.js
  2. export default class LoginPage {
  3. constructor() {
  4. this.userNameLocator = 'input[name=username]'
  5. this.passwordLocator = 'input[name=password]'
  6. this.formLocator = 'form'
  7. this.url = 'http://localhost:7077/login'
  8. }
  9. get username() {
  10. return cy.get(this.userNameLocator)
  11. }
  12. get password() {
  13. return cy.get(this.passwordLocator)
  14. }
  15. get form() {
  16. return cy.get(this.formLocator)
  17. }
  18. isTargetPage() {
  19. cy.visit('/login')
  20. cy.url().should('eq', this.url)
  21. }
  22. login(username, pwd) {
  23. this.username.type(username)
  24. this.password.type(pwd)
  25. this.form.submit()
  26. }
  27. }

跳转页面代码

当登录成功后,页面将跳转至 mainPage 页面,上面只写了 login 页面,这里写下跳转后的页面

  1. // login.js
  2. export default class mainPage{
  3. constructor() {
  4. this.h1Locator = 'h1'
  5. this.url = 'http://localhost:7077/dashboard'
  6. }
  7. get welComeText() {
  8. return cy.get(this.h1Locator)
  9. }
  10. isTargetPage() {
  11. cy.url().should('eq', this.url)
  12. }
  13. }

测试用例代码

  1. context('登录测试,PO 模式', function () {
  2. const username = 'jane.lane'
  3. const pwd = 'password123'
  4. it('登录成功', function () {
  5. // 创建 po 实例
  6. const loginInstance = new LoginPage()
  7. loginInstance.visitPage()
  8. loginInstance.isTargetPage()
  9. cy.login(username, pwd)
  10. cy.url().should('include', '/dashboard')
  11. const manInstance = new mainPage()
  12. manInstance.isTargetPage()
  13. manInstance.welComeText.should('contain', 'jane.lane')
  14. });
  15. })

测试结果

Cypress系列(62)- 改造 PageObject 模式 - 图3

总结下

  • login.js 和 mainPage.js 两个页面对象都有一个 isTargetPage() 函数来判断当前页面 URL 是否正确
  • 那这里就将每个 page 都共用的部分再次剥离,放到一个新的 common page
  • 然后每个 page 都继承自 common page(类似 selenium po 模式的 BasePage)

使用 common page 的栗子

commonPage.js 的代码

它也在 pages 文件夹下创建

  1. export default class commanPage {
  2. constructor() {
  3. // 构造函数可以为空
  4. // 如果不为空 应该是所有 page 都会用到的变量
  5. }
  6. isTargetPage() {
  7. cy.url().should('eq', this.url)
  8. }
  9. }

login.js 的代码

  1. // login.js
  2. import commanPage from "./commonPage";
  3. // 继承 commonPage
  4. export default class LoginPage extends commanPage{
  5. constructor() {
  6. // 调用父类的构造方法
  7. super()
  8. this.userNameLocator = 'input[name=username]'
  9. this.passwordLocator = 'input[name=password]'
  10. this.formLocator = 'form'
  11. this.url = 'http://localhost:7077/login'
  12. }
  13. get username() {
  14. return cy.get(this.userNameLocator)
  15. }
  16. get password() {
  17. return cy.get(this.passwordLocator)
  18. }
  19. get form() {
  20. return cy.get(this.formLocator)
  21. }
  22. visitPage(){
  23. cy.visit('/login')
  24. }
  25. login(username, pwd) {
  26. this.username.type(username)
  27. this.password.type(pwd)
  28. this.form.submit()
  29. }
  30. }

mainPage.js 的代码

  1. // login.js
  2. import commonPage from "./commonPage";
  3. // 继承 commonPage
  4. export default class mainPage extends commonPage {
  5. constructor() {
  6. super()
  7. this.h1Locator = 'h1'
  8. this.url = 'http://localhost:7077/dashboard'
  9. }
  10. get welComeText() {
  11. return cy.get(this.h1Locator)
  12. }
  13. }

测试结果

测试结果和上面的栗子一样

Cypress 使用 PO 模式的总结

  • Cypress 完全支持 PageObject 模式
  • 但存在一个问题,如果一个测试需要访问多个页面对象,就意味着测试中要初始化多个页面对象实例(new Page())
  • 如果一个页面对象需要登录才能访问(大部分场景都是这样),则每次初始化都需要先登录再访问(只有登录后才能重用 cookie),这无形增加了测试运行的时间
  • Cypress 不认为 PO 模式是一个好模式,它认为跨页面共享逻辑是一个反逻辑,因为 Cypress 的实现原理与其他工具完全不同
  • 那 Cypress 用什么方式来替代 PO 模式呢?答案就是下一篇要讲到的 Custom Commons

https://www.cnblogs.com/poloyy/p/13804226.html