共 7717 字,讀完需 10 分鐘,速讀需 5 分鐘,適合中高階前端工程師,本文對主流的 E2E 測試框架做了簡單對比,期望對大家的技術選型有幫助。
測試,尤其是自動化測試在現代 WEB 工程中有著非常重要的角色,與交付過程整合良好的自動化測試流程可以在新版釋出時幫你快速回歸產品功能,也可以充當產品文件。測試因粒度不同又可以分為單元測試、介面測試、功能測試。在 WEB 領域,功能測試亦稱為端到端測試(End to End Test,簡稱 E2E 測試),筆者在本文中會結合自身實踐和 GitHub 趨勢對比最受歡迎的 Node.js E2E 測試解決方案,首先我們按 GitHub 的 star 總數量排序,取前 5 名列舉如下(注意:你閱讀本文時 star 的數量可能已經不是最新的)。
- CasperJS — 6460 個 star,官網,程式碼,最近更新於 7 天前;
- Protractor — 6408 個 star,官網,程式碼,最近更新於 10 天前;
- Nightwatch.js — 6121 個 star,官網,程式碼,最近更新於 6 天前;
- TestCafe — 2268 個 star,官網,程式碼,最近更新於 1 天前;
- CodeceptJS — 1087 個 star,官網,程式碼,最近更新於 7 天前;
然後分別從環境搭建、測試編寫、測試報告等方面來直觀展示這 5 個 E2E 測試框架,期望能夠對做測試框架選型的同學有幫助。為了更客觀的體現各測試框架的特點,筆者設計了一些包含 E2E 測試中常用操作的測試用例,分別使用不同的框架來編寫。E2E 測試的常用操作如下:
- 開啟網頁,跳轉網頁:開啟 Github 的首頁;
- 填寫輸入框,提交表單:鍵入搜尋詞,提交搜尋表單;
- 元素單擊等操作:單擊搜尋結果的第一項;
- 元素數量、屬性檢視:確認搜尋結果展示了 10 條;
- 頁面執行環境檢視:確認頁面的地址是正確的;
CasperJS
CasperJS 是 star 數最高的測試框架,也是筆者最早開始採用的 E2E 測試框架,使用 Python 編寫,雖不算是嚴格意義上的原生 Node.js 解決方案,但因為能夠使用 npm 安裝,且能夠很好的與 Node.js 工具鏈組合使用,筆者還是把它列在了這裡。其特別之處在於只能與無介面瀏覽器(Headless Browser)組合使用,比如 PhantomJS 和 SlimerJS,這也讓 CasperJS 的優勢顯而易見:測試執行速度比真實瀏覽器快不少,且你不需要在持續整合系統中安裝各種瀏覽器或者某個瀏覽器的不同版本;潛在的坑在於,無介面瀏覽器的表現有時和真實瀏覽器不完全相同,會帶來某些難以排查解決的瀏覽器相容問題。
安裝步驟
- 安裝 Python 2.6 或更高版本
- 安裝 PhantomJS:npm install -g phantomjs
- 安裝 CasperJS:npm install -g casperjs
編寫測試
如果使用 ES6 之前的風格來編寫 CasperJS 測試,程式碼看起來會顯得非常臃腫,而實際上 CasperJS 也不支援任何 ES6/ES7 的新語法,除非你在執行測試之前自己對程式碼進行預編譯,實際程式碼如下:
casper.test.begin(`Github Search`, function suite(test) {
casper.start(`https://github.com`, function () { // 開啟首頁
test.assertVisible(`.js-site-search-form`, `should search input visible`);
this.fill(`.js-site-search-form`, { q: `casperjs` }, true); // 鍵入搜尋詞、並提交
});
casper.then(function () {
test.assertEval(function() { // 確認搜尋結果是 10
return __utils__.findAll(`.repo-list-item`).length >= 10;
}, `should show 10 results`);
});
casper.then(function () {
this.click(`.repo-list-item h3 a`); // 點選第1條結果
});
var location = null;
casper.then(function () { // 這裡是取環境變數
test.assertVisible(`.repository-content`, `should repo detail visible`);
location = this.evaluate(function () {
return window.location;
});
});
casper.then(function () { // 確認目前跳轉到了 casperjs 官方倉庫
test.assertEquals(location.pathname, `/casperjs/casperjs`, `should casperjs repo found`);
});
casper.run(function () {
test.done();
});
});複製程式碼
因為 CasperJS 對 CoffeeScript 有天然的支援,熟悉 CoffeeScript 的同學可以嘗試使用 CoffeeScript 編寫測試,或者使用這個工具轉換為 CoffeScript。
執行測試
casperjs test casperjs/test.js
檢視報告
測試通過
測試失敗
Protractor
Protractor 是 Angular 官方正在使用的 E2E 測試框架,可以說是專門為 Angular 定製,內建了各種可以選擇、操作 Angular 元素的便捷方法,如果你的應用基於 Angular 開發,使用它可以減少很多重複程式碼(顯然類似的便利在其他框架中也有支援)。對於 Angular 的重度使用者,Protractor 會是非常明智的選擇,不同於 CasperJS 的是 Protractor 在真實瀏覽器中執行測試程式碼。此外,Protractor 內建的頁面載入等待的功能,在 CasperJS 中需要自己設定合理的超時。相比於本文列出的其他框架,Protractor 的明顯優勢是測試用例的組織方式可以自由使用 Jasmine 或者 Mocha。
安裝步驟
- 安裝 JDK
- 安裝 Protractor:npm install –g protractor
- 初始化 WebDriver Manager:webdriver-manager update
- 建立配置檔案
編寫測試
Protractor 預設開啟了等待 Angular 載入並初始化完成的功能,如果你測試的不是 Angular 應用,則需要關閉這個功能,測試程式碼示例如下:
describe(`angularjs homepage todo list`, function () {
browser.ignoreSynchronization = true; // 不啟用智慧等待,因為 github 不是用 angluar 編寫的
browser.get(`https://github.com`);
it(`should search input visible`, function () {
var searchInput = element(by.className(`js-site-search-focus`));
var searchForm = element(by.className(`js-site-search-form`));
expect(searchInput.isDisplayed()).toEqual(true);
searchInput.sendKeys(`protractor`);
searchForm.submit();
});
it(`should show 10 results`, function () {
var searchList = element.all(by.className(`repo-list-item`));
expect(searchList.count()).toEqual(10);
element(by.css(`.repo-list-item h3 a`)).click();
});
it(`should repo detail visible`, function () {
var repoContent = element.all(by.className(`repository-content`));
expect(repoContent.isDisplayed()).toEqual([true]);
});
it(`should protractor repo found`, function (done) {
browser.executeScript(function () {
return window.location;
}).then(function (location) {
expect(location.pathname).toEqual(`/angular/protractor`);
done();
});
});
});複製程式碼
執行測試
- 執行 WebDriver Manager: webdriver-manager start
- 執行測試:protractor protractor.config.js
檢視報告
測試通過
測試失敗
Nightwatch
同樣流行的 Nightwatch,可以認為是 Protractor 的主要競爭對手,使用 Nigthwatch 編寫的程式碼非常簡潔,但是你需要手動在測試程式碼中新增合適的等待來保障測試的穩定,而 Protractor 和 TestCafe 則提供了內建的支援;Nightwatch 的主要劣勢在於繁瑣的安裝步驟,可能部分同學看到這個安裝文件或者下面的安裝步驟就知難而退了。
安裝步驟
- 安裝 JDK,版本 7 以上
- 下載 Selenium: selenium-server-standalone-{VERSION}.jar,複製到測試目錄
- 下載 WebDriver for Google Chrome,複製到測試目錄
- 安裝 Nightwatch: npm install -g nightwatch
- 建立配置檔案,需要在配置中宣告 chromewebdriver 的地址;
編寫測試
module.exports = {
`Github Search`: function (browser) {
browser // 開啟首頁、填寫搜尋詞、提交搜尋表單
.url(`https://github.com`)
.assert.visible(`.js-site-search-focus`, `should search input visible`)
.setValue(`.js-site-search-focus`, `nightwatch`)
.submitForm(`.js-site-search-form`)
.pause(1000);
browser.execute(function () { // 確認展示 10 條搜尋結果
return document.querySelectorAll(`.repo-list-item`).length;
}, function (args) {
browser.assert.equal(args.value, 10, `should show 10 results`);
});
browser.click(`.repo-list-item h3 a`).pause(1000);
browser.assert.visible(`.repository-content`, `should repo detail visible`);
browser.execute(function () {
return window.location;
}, function (args) { // 確認開啟了 nightwatch 官網
browser.assert.equal(args.value.pathname, `/nightwatchjs/nightwatch`, `should nightwatch repo found`);
});
browser.end();
}
};複製程式碼
執行測試
- 執行 Selenium:java -jar selenium-server-standalone-3.0.0.jar
- nightwatch test.js
檢視報告
測試通過
測試失敗
TestCafe
TestCafe 是非常年輕但很受開發者歡迎的測試框架,因為不需要依賴 WebDriver 之類的東西,TestCafe 環境只需一鍵即可完成,這也意味著,你可以在任何安裝了瀏覽器應用的物理裝置上執行測試。TestCafe 對 ES6/ES7 語法的天然支援讓它更具前瞻性,命令列工具產生的測試報告簡潔但不失完整。由於開源的時間較短,相比於其他測試框架 TestCafe 的社群和生態還不夠成熟。儘管如此,不斷出現的各種 TestCafe 功能擴充套件也證明了它的社群和生態在不斷壯大。對於站在 WEB 技術風口浪尖的同學,TestCafe 無疑是非常值得留意的 E2E 測試解決方案,開箱即用的特性極大的降低了使用者的成本。
安裝步驟
npm install testcafe -g複製程式碼
編寫測試
TestCafe 的測試組織方式詳見這裡,選擇符支援也非常強大,支援類似於 jQuery 的靈活非同步的選擇符,斷言風格非常類似 Chai,下面是測試程式碼:
import { Selector } from `testcafe`;
fixture `Github Search`
.page `https://github.com`;
test(`should github search work as expected`, async t => {
const searchInput = Selector(`.js-site-search-focus`);
const searchList = Selector(`.repo-list-item`);
const resultItem = Selector(`.repo-list-item h3 a`);
const repoContent = Selector(`.repository-content`);
await t.setTestSpeed(0.8);
await t.expect(searchInput.exists).eql(true, `should search input visible`);
await t.typeText(searchInput, `testcafe`);
await t.pressKey(`enter`);
await t.expect(searchList.count).eql(10, `should show 10 results`);
await t.click(resultItem);
await t.expect(repoContent.exists).eql(true, `should repo detail visible`);
const location = await t.eval(() => window.location);
await t.expect(location.pathname).eql(`/DevExpress/testcafe`, `should testcafe repo found`);
});複製程式碼
執行測試
testcafe chrome testcafe/test.js複製程式碼
檢視報告
測試通過
測試失敗
CodeceptJs
CodeceptJs 可能並不算是嚴格意義的 E2E 測試框架,它對各種測試執行工具做了一層封裝,旨在提供更簡潔的 API,你可以自由選擇下面這些測試執行工具:
- WebDriverIO
- Protractor
- Selenium WebDriver JS
- NightmareJS
CodeceptJs 讓筆者比較欣賞的地方在於測試用例的組織,基於 Feature 和 Scenario 兩個粒度來組織測試讓它看起來更有 E2E 測試的樣子,它支援最新的 ES6 語法,同時也遮蔽各種複雜的回撥細節,所有的測試用例都是以第一人稱來做,讓測試程式碼閱讀起來更像是自然語言,而讓筆者擔憂的地方在於,過多的封裝可能導致出問題時排查比較困難。
安裝步驟
- 安裝 CodeceptJs npm install -g codeceptjs
- 用命令列工具初始化配置 codecept.json:codeceptjs init
- 使用命令列工具生成測試:codeceptjs gt
- 此外,你需要安裝你所選擇的底層測試工具,比如 WebDriver、Protractor
編寫測試
Feature(`Github Search`);
Scenario(`search codecept repo`, (I) => {
I.amOnPage(`https://github.com`);
I.seeElement(`.js-site-search-focus`);
I.fillField(`.js-site-search-focus`, `codeceptjs`);
I.pressKey(`Enter`);
I.seeElement(`.repo-list-item`);
I.click(`.repo-list-item h3 a`);
I.seeElement(`.repository-content`);
I.seeInCurrentUrl(`/Codeception/CodeceptJS`);
});複製程式碼
複製程式碼
執行測試
codeceptjs run複製程式碼
檢視報告
測試通過
測試失敗
總結對比
本文中的所有程式碼可以在 GitHub 倉庫上看到。任何開發工具的演化都是朝著更快捷、高效的目標。本文介紹的幾個 E2E 測試框架可以說各有所長,在做框架選型的時候該考慮哪些因子呢?這些因子的優先順序如何?下面是筆者的考慮:
- 上手簡單:P0,環境搭建步驟?出錯的概率?是否需要繁瑣的配置?TestCafe 非常簡單;
- 文件支援:P0,是否包含入門文件?API 參考?開發者文件?本文中的五款都還不錯;
- 過程透明:P0,測試執行過程是否是透明的,能否觀察到頁面行為?CasperJS 就像個黑盒;
- 執行速度:P0,測試執行速度能決定你 CI 流程的好壞;
- 測試報告:P0,是否支援多種報告?是否方便與 CI 流程整合?比如要支援 XML、JUnit 等格式輸出;
- 除錯功能:P0,出錯時提供的資訊是否清晰?是否支援截圖?TestCafe 做的很好;
- 測試組織:P1,是否能與現有的技術棧很好的組合起來?不能結合獨創的方式是否直觀?如果是大型專案可以提高優先順序,Protractor 和 CodeCeptJs 佔優;
- 程式碼風格:P1,簡潔的程式碼意味的更高的可讀性、更低的維護成本;TestCafe、CodeCeptJs 不錯;
- 社群支援:P2,圍繞這個工具是否有成熟的社群?可以用來提問、貢獻程式碼;
- 可擴充套件性:P3,是否支援 API 擴充套件?擴充套件成本如何?
如果你的專案中需要新增 E2E 測試,做決定的時候沒有標準答案,因為還需要結合專案自身的特點,比如規模大小、對上面各因子的要求。
備註:本文的初始版本來源於 Medium 上的文章,但是筆者在原文基礎上重新設計了測試用例,每個測試框架的介紹也參與了筆者自身的使用經驗,框架選型上也融入了自己的思考,有興趣的可以去看原文。
One More Thing
本文作者王仕軍,商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。如果對文中的內容有任何疑問,歡迎留言討論。想知道我接下來會寫些什麼?歡迎訂閱我的專欄,點選文章下方的關注即可。