Nightwatch101 #22:Page Objects

Nightwatch.js

Page Objects 預先定義一個頁面裡面的元素(Element)、區塊(Section)和命令(Command),意即將一個網頁切分成許多個片段,利用物件把這些片段包裝起來使用,例如:將一個頁面區分為 header、footer 等,然後在測試程式中就用這樣的 header 或 footer 為單位操作 DOM element。

好處有二

♡(´∀`)人(´∀`)♡

本系列文章皆使用這個專案,可以拉下來玩玩;有什麼問題都可以提出 issue


如何使用 Page Objects?

Step 1:新增資料夾存放 Page Object

每一個 Page Object 必須放在獨立的一支檔案,並放在指定資料夾內。

範例檔案結構如下所示,這個有一個專門放 Page Object 的資料夾 page_objects,裡面有兩支檔案 findPage.js 和 goodsPage.js,它們分別代表了搜尋頁和商品頁的網頁片段。

nightwatch101
  ├── custom_assertions
  ├── custom_commands
  ├── page_objects (放置 Page Object)
  |   ├── findPage.js (搜尋頁)
  |   └── goodsPage.js (商品頁)
  ├── reports
  ├── screenshots
  ├── test/e2e
  ├── globals.js
  ├── nightwatch.conf.js
  ├── package.json
  └── README.md

Step 2:設定檔案路徑

Nightwatch Test Runner 必須要知道 Page Objects 的所在位置,因此在 nightwatch.conf.jspage_objects_path 設定檔案路徑,可為字串或陣列。

const config = {
  "src_folders": [
    "test/e2e"
  ],
  "page_objects_path": './page_objects',
  },
  // ...
}

完整範例可參考這裡

Step 3:開始撰寫 Page Objects

我們會拿一支檔案來將特定網頁封裝成物件,例如,將露天拍賣的搜尋頁定義成一個 findPage.js 的 Page Objects。將要抓取操作的 DOM element,使用 elements 屬性預先設定好,其中可設定要選取的網頁元素和定位的策略,定位策略可為 CSS Selector(預設)或 Xpath。

module.exports = {
  elements: {
    searchbox: {
      selector: '#search_input' // 要選取的網頁元素
    },
    submit: {
      selector: '//[@name="q"]',
      locateStrategy: 'xpath' // 定位策略是 Xpath
    }
  }
};

Step 4:在測試程式中使用 Page Objects

引入這個 Page Objects。

require('./page_objects/findPage.js')

接著,測試程式中若要使用這個元素,使用 @ 當前綴標記即可。

原先是這樣

browser
  .url('http://find.ruten.com.tw/search/s000.php?enc=u&searchfrom=indexbar&k=Pusheen&t=0')
  .click('.rt-site-search-submit') // 點擊提交按鈕

改用 Page Objects 就會成這樣

findPage
  .navigate()
  .click('@submit') // 點擊提交按鈕,但這個按鈕被封裝起來

Step 5:測試範例

測試範例如下

範例程式碼。

module.exports = {
  'Find Pusheen': function (client) {
    var findPage = client.page.findPage();

    findPage.navigate()
      .assert.title('搜尋結果 : Pusheen - 露天拍賣')
      .assert.visible('@searchbox')
      .clearValue('@searchbox')
      .setValue('@searchbox', 'Pusheen')
      .click('@submit');

    client.end();
  }
};

完整測試程式

執行測試,來搜尋關於 Pusheen 的商品。

nightwatch test/e2e/find/findPusheen.js

搜尋結果 : Pusheen - 露天拍賣

執行結果。

Page Objects

url 屬性

設定靜態網址。

module.exports = {
  url: 'http://find.ruten.com.tw/search/s000.php', // 靜態網址
  elements: {}
};

設定動態網址。

module.exports = {
  url: function() {
    return this.api.launchUrl + '/search/s000.php'; // 動態網址
  },
  elements: {}
};

撰寫指令

使用 command 這個屬性來增加指令,這樣封裝的方式讓程式碼更簡單明瞭。Nightwatch 所提供的指令、斷言等,使用 this 即可取得。其中,this.api / this.waitForElementVisible 用於在離開 Page Objects 後,繼續使用鍊結呼叫的方式使用 client 底下的其他 API。

這裡撰寫一個負責提交表單的指令「submit」,執行這個指令時,會先等待 1 秒,然後檢視提交按鈕 .rt-site-search-submit 這個 DOM element 是否可見,最後點擊這個按鈕。

定義 Page Objects。

var findCommands = {
  submit: function() {
    this.api.pause(1000);
    return this.waitForElementVisible('@submit', 1000)
      .click('@submit')
  }
};

module.exports = {
  url: 'http://find.ruten.com.tw/search/s000.php?enc=u&searchfrom=indexbar&k=Pusheen&t=0',
  commands: [findCommands],
  elements: {
    searchbox: {
      selector: '#search_input'
    },
    submit: {
      selector: '.rt-site-search-submit'
    }
  }
};

測試程式。

module.exports = {
  'Find Pusheen': function (client) {
    var findPage = client.page.findPage();

    findPage.navigate()
      .assert.title('搜尋結果 : Pusheen - 露天拍賣')
      .assert.visible('@searchbox')
      .clearValue('@searchbox')
      .setValue('@searchbox', 'Pusheen')
      .submit(); // 客製化指令

    client.end();
  }
};

完整程式碼

執行測試程式。

nightwatch test/e2e/find/findPusheenCustomCommands.js

執行結果。

撰寫指令

總結

Page Objects 的使用方式如下

在優化測試程式碼上,Page Object 可將網頁片段封裝起來成為物件,可重用、增加可讀性,減少維護的複雜度,應多多使用。

備註:若要使用新版的 Page Object(v0.7+),必須使用 elements 或 sections 任一屬性,否則會被視為舊版。

推薦閱讀


下一篇來看如何使用 Sections 優化 Page Objects。


2018 鐵人賽網址


Nightwatch End-to-End Testing 端對端測試 自動化測試 Nightwatch101 Selenium 鐵人賽 2018鐵人賽 Nightwatch101 2018 iT 邦幫忙鐵人賽 系列文