使用 testCafe 做E2E測試

End-To-End Testing 在自動化測試中,扮演著很重要的一個腳色,以往在單元測試的部分,若與外部溝通,通常我們會運用 mock、stub 來對依賴關係做一個隔離
但是在 End-To-End 測試中,反而是盡量避免使用 mock、stub,為的是要模擬使用者操作網站介面,來驗證我們的網站功能是否正確

testcafe

基於 node.js 的自動化端對端測試工具

優點

  1. 支援主流平台:Mac、Linux、Windows
  2. 安裝簡易、一分鐘搞定 npm install testcafe -g
  3. open source

功能

  1. 支援 ES6 與 typeScript,所以可以直接用 typescript 寫測試,配合 VSCode,享受強型別及 intellisense
  2. 提供 Live Runner 模式,監測檔案變更自動重跑測試
  3. 自動等候網頁載入完成及 XHR,這個真的很方便,省去很多麻煩
  4. 並行測試,可同時開啟同個瀏覽器的多個實體跑測試,加快速度
  5. 支持 page object pattern,並附上範例教學

持續交付

官方文件也提供各式工具的文章說明,此部分請自行查看Continuous Integration

Report

報告的格式,testcafe 是直接做成 npm 套件,文件上也有說要怎麼自行建立喜歡的報告格式,但是我還是直接在NPM上搜尋人家做好的比較方便

比較漂亮的有一份是Cucumber JSON TestCafe Reporter,這一套實際上是幫你產一個 Json 出來,然後用另外一套報告產生器來讀這份資料

使用上就是直接下命令即可,這邊的reporter-app-namereporter-app-version會直接顯示在報告內的 App 欄位那邊,產出的 json 則是被指定到reports/report.json

1
testcafe chrome ./path-to-tests/*(.js|.testcafe|.ts) --reporter cucumber-json:reports/report.json --reporter-app-name='My App' --reporter-app-version='x.y.z'

產生 Html report 則還需要先安裝套件

1
npm install --save-dev multiple-cucumber-html-reporter

接著建立一個report-generator.js,照著貼就行了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const report = require("multiple-cucumber-html-reporter");
const path = require("path");
const projectName = path.basename(__dirname);
const projectVersion = process.env.npm_package_version;
const reportGenerationTime = new Date().toISOString();
report.generate({
reportName: "TestCafe Report",
jsonDir: "reports",
reportPath: "reports",
openReportInBrowser: true,
disableLog: true,
displayDuration: true,
durationInMS: true,
customData: {
title: "Run info",
data: [
{ label: "Project", value: `${projectName}` },
{ label: "Release", value: `${projectVersion}` },
{ label: "Report Generation Time", value: `${reportGenerationTime}` }
]
}
});

最後,在package.json之內加一個別名

1
"report": "node report-generator.js",

這樣子就可以先

  1. 產生數據
  2. 透過數據產生報告

如何撰寫測試

細節就不說明了,官方文件照著參考就行,這邊提幾個記憶點

page-model

如果還不熟悉,那先撰寫測試好些,然後呢,順利跑過之後,再想著重構。別一開始就想著重構重構、到最後拆得亂七八糟,調整個需求都差點忘記去哪裡改的話,那還不如別重構呢。

文件上說的支援 page-object 的方式,其實也就是以前 91 上課曾經提到過的將程式與 UI 中間再加一層,避免它們彼此相依,這樣若 UI 有變動,程式碼是不會受影響的,看著文件倒是又重新溫習了一次

程式碼可以參考一下GitHub:mwq27/testcafe-page-model-example,參考這份程式碼直接利用 typeScript 撰寫,搭配 VSCode 也是不錯的選擇

網站登入

可以將登入方法封裝於 pageObject 內重複使用,也可以利用Role()的方式來設定目前要使用的身分,這樣要用哪一個身分來測試網站,就直接在測試中調用t.useRole(role),在測試上應該是挺語意化的。

埋 DOM 的 data-test 屬性

如果不想要未來改變 HTML 屬性影響到測試,那就最好將要測試的 DOM,給他一個測試用的選取方式,例如 <span data-test="result">--</span>
這樣做的好處是測試中的Selector會清晰很多,而且測試程式也不再與 DOM 的 id、name 掛勾

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 方法1
class OrderHistory {
EditButton: Selector = Selector(
"#orderTable > tbody > tr > td > div > div.row.order-history-info__table-row.ml-0.mr-0 > div:nth-child(14) > a"
);
async ShowOrderDetail(browser: TestController) {
await browser.click(this.EditButton);
}
}

// 方法2
class OrderHistory {
EditButton: Selector = Selector("[data-test='editButton']");
async ShowOrderDetail(browser: TestController) {
await browser.click(this.EditButton);
}
}