测试自动化_博客已迁移软件测试自动化测试

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

2017-10-20  本文已影响49人  做测试的DanteYu

本文章基于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');
    }
  });
}
上一篇下一篇

猜你喜欢

热点阅读