codeceptjs学习笔记 - Page Object设计模式

本文章基于codeceptjs@1.0.1

这篇文章会介绍UI测试最常用的设计模式在codeceptjs的应用 - Page Object

Benefit

Page Object有哪些好处?

  • 代码重用。可以在多个测试场景里面重用代码,减少重复的代码
  • 易维护。如果系统有变动,那么只需要在一个地方进行修改

codeceptjs的Page Object

codeceptjs gpo可以帮助你快速创建模板和进行配置

➜  codeceptjs-demo git:(master) ✗ codeceptjs gpo
Creating a new page object
--------------------------
? Name of a page object LandingPage
? Where should it be stored ./pages/LandingPage.js
Updating configuration file...
Page object for LandingPage was created in /Users/diyu/workspace/codeceptjs_demo/codeceptjs-demo/pages/LandingPage.js
Use landingPagePage as parameter in test scenarios to access it

模板文件会被创建在项目根目录下的pages目录中,内容如下

'use strict';
let I;

module.exports = {

  _init() {
    I = actor();
  }

  // insert your locators and methods here
}

把这个文件补充完整,就会变为下面这样

'use strict';
let I;

module.exports = {

  _init() {
    I = actor();
  },

  // setting locators
  fields: {
    email: '#user_basic_email',
    password: '#user_basic_password'
  },
  submitButton: {css: '#new_user_basic input[type=submit]'},

  // introducing methods
  sendForm(email, password) {
    I.fillField(this.fields.email, email);
    I.fillField(this.fields.password, password);
    I.click(this.submitButton);
  }
}

上面的page object文件写好之后,我们应该把它加入到测试文件中

Scenario('login', (I, loginPage) => {
  loginPage.sendForm('john@doe.com','123456');
  I.see('Hello, John');
});

还需要加入到在配置文件中

"include": {
  "I": "./steps_file.js",
  "landingPage": "./pages/landingPage.js"
}

generator在page object中的应用

我们可以在po文件中通过generator来使用grab*()方法

'use strict';
let I;

module.exports = {

  _init() {
    I = actor();
  },

  // setting locators
  container: "//div[@class = 'numbers']",
  mainItem: {
    number: ".//div[contains(@class, 'numbers__main-number')]",
    title: ".//div[contains(@class, 'numbers__main-title-block')]"
  },

  // introducing methods
  openMainArticle: function* () {
    I.waitForVisible(this.container)
    let _this = this
    let title;
    yield within(this.container, function*(){
      title = yield I.grabTextFrom(_this.mainItem.number);
      let subtitle = yield I.grabTextFrom(_this.mainItem.title);
      title = title + " " + subtitle.charAt(0).toLowerCase() + subtitle.slice(1);
      yield I.click(_this.mainItem.title)
    })
    return title;
  }
}
Scenario('login2', (I, mainPage, basePage) => {
  let title = yield* mainPage.openMainArticle()
  basePage.pageShouldBeOpened(title)
});

PageFragment

PageFragment是指在页面比较独立的部分,比如页面中一些独立组件(i.e. 弹出框)和小工具。我们可以把这可能每个页面都在用的公共部分抽取出来,单独的处理。

使用codeceptjs go --type fragment能够创建模板文件

➜  codeceptjs-init codeceptjs go --type fragment
Creating a new fragment object
--------------------------
? Name of a fragment object LandingPage
? Where should it be stored ./fragments/LandingPage.js
Updating configuration file...
Fragment object for LandingPage was created in /Users/diyu/workspace/codeceptjs_demo/codeceptjs-init/fragments/LandingPage.js
Use landingPageFragment as parameter in test scenarios to access it

配置文件如下

"include": {
  "I": "./steps_file.js",
  "landingPagePage": "./pages/landingPage.js",
  "landingPageStep": "./steps/LandingPage.js",
  "landingPageFragment": "./fragments/LandingPage.js"
},

对于page fragment推荐的使用方式是包含一个组件的root locator,然后在page fragment中的方法使用within()去缩小范围。

let I;
// fragments/modal.js
module.exports = {

  _init() {
    I = actor();
  },

  root: '#modal',

  // we are clicking "Accept: inside a popup window"
  accept() {
    within(this.root, function() {
      I.click('Accept');
    });
  }
}

StepObjects

StepObjects代表涉及多个页面的复杂操作流程,实现了业务价值。

使用codeceptjs go --type step能够创建模板文件。

➜  codeceptjs-init codeceptjs go --type step
Creating a new step object
--------------------------
? Name of a step object LandingPage
? Where should it be stored ./steps/LandingPage.js
Updating configuration file...
Step object for LandingPage was created in /Users/diyu/workspace/codeceptjs_demo/codeceptjs-init/steps/LandingPage.js
Use landingPageStep as parameter in test scenarios to access it

配置文件如下:

"include": {
  "I": "./steps_file.js",
  "landingPagePage": "./pages/landingPage.js",
  "landingPageStep": "./steps/LandingPage.js"
}
let I, userPage, permissionPage;
module.exports = {

  _init() {
    I = actor();
    userPage = require('../pages/user');
    userPage._init();
    permissionPage = require('../pages/permissions');
    permissionPage._init();
  },

  createUser(name) {
    // action composed from actions of page objects
    userPage.open();
    userPage.create(name);
    permissionPage.activate(name);
  }
};

Actor

在我们刚开始初始化的时候 codeceptjs init,有一个步骤会问你需要custom step files不。这个文件的目的是可以让你通过steps_file.js来扩展I对象的方法。

我们在steps_file.js中加入新的一个方法foo()

'use strict';
// in this file you can append custom step methods to 'I' object

module.exports = function() {
  return actor({

    // Define custom steps here, use 'this' to access default methods of I.
    // It is recommended to place a general 'login' function here.

    foo : function(){
        console.log('I am customized step foo for object I');
    }

  });
}

然后可以在测试文件中直接作为I的方法来进行调用了。

Feature('within demo');

Scenario('login github', {retries: 2}, (I, landingPage)=>{
    I.amOnPage('http://www.baidu.com');
    I.foo();
    landingPage.land();
    // I.see('test');
    I.say('Above is the link for baidu search engine');
});

要想在steps_file.js中使用I对象,需要使用self代替

'use strict';
// in this file you can append custom step methods to 'I' object

module.exports = function() {
  return actor({

    login: function(email, password) {
      this.fillField('Email', email);
      this.fillField('Password', password);
      this.click('Submit');
    }
  });
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 173,637评论 25 708
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 135,001评论 19 139
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,779评论 18 399
  • Android SDK API 19+ 开始支持 WebView的远程调试,配合 Chrome 的开发者工具可以...
    act262阅读 790评论 0 0
  • 某一天 遇见了你 我便不要天上的月亮了 只想要拥有尘世的幸福了
    伍月的晴空阅读 142评论 2 5