展望2018 年JavaScript Testing

展望2018 年JavaScript Testing

- 20180227更新-
原作者於2月9號更新了文章,主要是調整文章結構、新增加了新的框架和更新附錄中的推薦閱讀,並將標題更改為'An Overview of JavaScript Testing in 2018'。譯文內容也做出更新。

原文鏈接: An Overview of JavaScript Testing in 2018本譯文已獲原作者授權翻譯

原作者: vzaidman⎝(•ω•)⎠!!JS

譯者註:去年底看到了stateofjs中關於測試的盤點,發現不少陌生的名字,在medium上找到這篇文章,雖然文章發布日期是4月,但是盤點報告裡的測試框架也沒有巨變。時效性還是有的=)

簡而言之

單元測試和集成測試用Jest完成,UI測試則是用TestCafe。

這篇指南意在和開發者分享2018年JavaScript 測試領域最值得關注的框架、工具和方法論等。綜合多篇討論了類似話題的優秀文章(相關鏈接見文章結尾處)的結論,融合了我們在自己的產品Welldone Software Solutions上實踐出的經驗和想法,最終寫成了這篇文章。

仔細讀過這篇文章的開發者,可以大膽地說自己已經對今年JS 測試領域略知一二了。

  • 這篇文章是經過相當嚴謹的調研後出爐的,如果讀者發現其中出現了錯誤或者遺漏的地方,請在評論區告訴我,我將會第一時間更正。
  • 請注意文章底部的相關鏈接,把它們全部讀過並理解透徹了,理論上你將會從一個初學者進階為JS 測試大師。
  • 這篇指南最好的使用方式是任選一個或一些你需要的測試方法,找到匹配的測試工具,多做一些嘗試。我們身處於一個訊息爆炸工具繁多的領域。你的目標應該是根據項目情況篩選出合適的工具。

介紹

看,下圖是Facebook 出品的測試框架Jest 的logo:

他們的slogan標榜了Jest是“painless(沒有)”測試,但有人認為"沒有痛點的測試是不存在的":

因為一般來說JS開發者是不太喜歡做網頁測試的。 JS 測試往往能力有限、搭建測試用例比較難而且效率不高。

但其實,只要用對方法,找到合適的測試框架或測試庫的組合方式,是能構建出一套覆蓋完整且效率很高的方案的。

測試類型

想要了解更多關於測試類型的訊息,可以訪問這三個鏈接: 鏈接1鏈接2鏈接3

總的來看,可以分為下面三種測試類型:

  • 單元測試(Unit Test) -通過模擬輸入和預測輸出的方式測試獨立的函數或者類。
  • 集成測試(Integration Test) -測試多個模塊間的聯動是否和期望相同。
  • UI測試(也被稱為Functional Test) -關注點不在內部實現方式,而是測試產品在真實使用場景(比如在瀏覽器)中是否可以達到預想的結果。

測試工具的類型

根據功能,測試工具可以被分為下面幾類,其中有些專注在一個測試類型上;有些則是像搭積木一樣,開發者通過自由搭配不同的工具整合適合項目的測試方法。

即使用一個測試框架可能就能滿足當前需求,但出於長遠考慮,為了提高擴展性,多數開發者還是會選擇自由組合各種工具。

  1. 提供測試結構Mocha , Jasmine , Jest , Cucumber
  2. 斷言測試: Chai , Jasmine , Jest , Unexpected
  3. 生成、展示和監控測試結果: Mocha , Jasmine , Jest , Karma
  4. 通過對比生成的組件和數據結構的快照,確保更改是來自前一次運行的: Jest , Ava
  5. 提供Mocks、Spies和StubsSinon , Jasmine , enzyme , Jest , testdouble
  6. 生成代碼覆蓋報告: Istanbul , Jest , Blanket
  7. 提供一個瀏覽器或類瀏覽器環境,並提供接口可以控制它們的執行場景: Protractor , Nightwatch , Phantom , Casper

現在來展開聊聊以上工具吧:

測試結構(Testing Structure)指的是開發者如何組織自己的測試邏輯。常見的BDD( 行為驅動開發behavior-driven development )測試結構的代碼大概是這樣的:

describe('calculator',function(){// 内嵌 describe 函数用于描述一个模块
describe('add',function(){// 期望的表现行为
it('should add 2 numbers',function(){// 用断言测试预期行为
})})})

斷言函數:用於測試運行結果是否如預期的函數,使用人數最多的是下面代碼中的前兩個庫(即Chai和Jasmine):

// Chai中設定expect值expect(foo).to.be.a('string')expect(foo).to.equal('bar')// Jasmine中設定expect值expect(foo).toBeString()expect(foo).toEqual('bar')// Chai的斷言assert.typeOf(foo,'string')assert.equal(foo,'bar')// Jasmine中的expect值expect(foo,'to be a','string')expect(foo,'to be','bar')

TIP:對Jasmine的斷言的高級運用,可以看這篇文章

Spies告訴開發者在應用中或者測試中的函數被調用多少次、在什麼情況下被誰調用等訊息。這個特性常用在集成測試中,確保代碼執行時的副作用(比如性能消耗)在預期之內,特別是想要測試特定場景下函數的執行情況時。比如在使用應用的某個過程裡一個計算邏輯被調用了多少次。

it('should call method once with the argument 3',()=>{constspy=sinon.spy(object,'method')spy.withArgs(3)object.method(3)assert(spy.withArgs(3).calledOnce)})

Stubbing也可以叫dubbing (類似電影中「替身」的概念)的使用場景是開發者在確定某個模塊一定能通過測試時,假設那些函數已經被正確的執行了,然後將這些函數替換成預期值。

如果我們希望在測試時user.isValid()總是返回true ,那麼你可以這麼寫:

sinon . stub ( user , 'isValid' ). returns ( true ) // Sinon spyOn ( user , 'isValid' ). andReturns ( true ) // Jasmine

也支持promise 語法:

it('resolves with the right name',done=>{conststub=sinon.stub(User.prototype,'fetch').resolves({name:'David'})User.fetch().then(user=>{expect(user.name).toBe('David')done()})})

Mocks或者被稱為Fakes假定了某些模塊或者某些行為,以確保測試是在已知輸入值的情況下進行的

Sinon就有這個功能,比如模擬服務器和客戶端間的交互,保證能迅速得到預期的結果:

it('returns an object containing all users',done=>{//創建一個模擬服務器,代替真實的網絡請求constserver=sinon.fakeServer.create()server.respondWith('GET','/users',[200,{'Content-Type':'application/json'},'[{ "id": 1, "name": "Gwen" }, { "id": 2, "name": "John" }]'])Users.all().done(collection=>{constexpectedCollection=[{id:1,name:'Gwen'},{id:2,name:'John'}]expect(collection.toJSON()).to.eql(expectedCollection)done()})server.respond()server.restore()});

快照測試適用需要比較預期數據結果和實際結構的場景。比如,下面這段代碼模擬了鏈接組件被渲染後,將結果保存為JSON 格式以做比較。

下面的例子來自Jest官方文檔,對Link這個組件做快照測試:

it('renders correctly',()=>{constlinkInstance=(<Linkpage="http://www.facebook.com">Facebook</Link>)consttree=renderer.create(linkInstance).toJSON()expect(tree).toMatchSnapshot()})

它不會真的去渲染一個Link 組件然後截圖比較,而是把內部數據放在一個獨立的文件中:

exports[`renders correctly 1`] = ` <a className="normal" href="http://www.facebook.com" onMouseEnter={[Function]} onMouseLeave={[Function]} > Facebook </a> `;

執行測試i時如果發現前後兩份文件的內容有不同,它會和開發者確認這些不同點:

注意:雖然快照測試常見於比較組件的一致性,但其實很多需要比較數據一致性的場景也可以用這種測試,比如redux中的store對象、應用內不同組件的內部結構等等。

下面三種場景任意一種,都能被稱之為“瀏覽器或類瀏覽器環境”:

  • jsdom --一個純淨的模擬真實瀏覽器的JS環境,沒有UI不做任何界面渲染。但是它提供了window、document、body、location、cookies、選擇器等等所有在瀏覽器中運行的JavaScript 代碼可能用到的對象。
  • Headless瀏覽器環境--為了更快地執行代碼,提供了無界面的瀏覽器。
  • 真實的瀏覽器--在平時我們用的瀏覽器中執行你的測試代碼。

組合為整體

我們建議盡可能在所有的場景裡都用同一種測試工具,包括相同測試結構和語法(2)、斷言函數(3)、測試報告和監控(4) 。有時候即使只用一種環境配置(1)都可以滿足兩種以上的測試場景。

我們建議開發者將測試分為兩塊。一個執行單元和集成測試,另一個做UI測試。 UI 測試往往需要消耗更長時間,特別是在需要跨瀏覽器測試的場景中還需要用到一些(可能要求付費的)外部服務,以提供不同的設備和瀏覽器(這一點會在後續討論到)。所以可能開發者不會頻繁進行UI 測試,可能只需要在一些關鍵節點上(比如合併某個特性的分支時)再進行。

單元測試

需要覆蓋到應用所有的單元。為這些單元撰寫簡單的邊緣測試,用斷言(3)檢測輸出是否如預期。還要生成測試覆蓋報告(6) ,確認代碼的覆蓋率。

單元測試在函數式編程中相對容易測試得多。代碼中的純函數(pure function)越多,越容易進行單元測試。

集成測試

在以前,常常只做單元測試。這樣即使每個單元測試都通過了,但在應用整體執行的時候常常出現bug。集成測試(包括快照測試)可以防止開發者為了修復一個問題,又引入了新的問題而不自知的情況。另外,要知道用戶會用什麼樣的姿勢使用你的產品是未知的,幾乎無法測試到所有單元的所有應用場景。有些用例必須從更高維度的測試場景下才能覆蓋到。

集成測試需要覆蓋到關鍵的跨模塊進程。和單元測試相比,你可能需要用到Spies (5)確保副作用在可控範圍內;需要用Stubs(5)模擬不在本次測試中模塊,以確保此次測試能順利進行。

和單元測試不同的是,有時候我們的代碼需要渲染特定組件還要與之發生交互,瀏覽器或類瀏覽器環境(7)能提供window對象保證這些場景能被測試到。

組件快照測試(4)也屬於集成測試。它們為我們提供了當組件未被渲染的時候,代碼對它們會產生的影響是什麼樣的。也可以在瀏覽器或類瀏覽器環境中進行這方面測試。

UI測試

雖然單元測試和集成測試性價比頗高,但也有它們無能為力的時候。前文也提到過,UI測試通常需要在瀏覽器或類瀏覽器環境(7)中執行。

UI 測試可以在特定環境中模擬用戶行為(比如點擊、打字、滾動...),而且這些模擬都是從終端用戶的視角出發的。

UI 測試是所有測試中最難部署的。想像一下,你需要搭建一個環境讓一個測試可以在不同的機器、不同設備和不同版本不同廠商的瀏覽器中運行。開發這麼一個測試環境的成本極大,這也是為什麼會有專門提供UI測試的產品

通用的測試工具

JSDom是超文本DOM規範和HTML標準的JS實現。換句話說,JSDom 是用純JS 模擬了瀏覽器環境。

在這個模擬環境下,代碼執行效率極高。但JSDom 的短板也正是無法百分之百模擬瀏覽器行為(比如無法用它截圖),所以用JSDom 可能會限制你的測試範圍。

值得一提的是,JS 社區響應迅速,它的能力不斷提升,最新版本已經很接近真正的瀏覽器了。

Istabul能夠將開發者所寫的測試用例的覆蓋率反饋出來。它分別對聲明、行、函數和分支都做了覆蓋檢測,在生成的報告中以百分比的形式展示,開發者可以直觀地看到是那部分代碼還需要進一步測試。

Karma允許測試直接運行在瀏覽器環境下。這個環境包括了真正的瀏覽器、Phantom、 JSDom甚至是非常老的瀏覽器(譯者註:比如還需要ActiveX的IE們)。

Karma 會啟動一個測試服務器,服務器發送某個特定的web 頁面到客戶端,作為開發者的測試環境。這個頁面將會在多個瀏覽器上打開。

這也意味著,通過BrowserStack的配合,Karma就能遠程調試。

Chai是目前最受歡迎的斷言測試庫。

Unexpected也是一個斷言庫,它的語法和Chai有一點不同。 Unexpected也有良好的擴展性,通過各種插件(比如unexpected-react )讓斷言能力進一步提高,想了解更多請訪問這裡

Sinon是一個只做spies、stubs和mocks這三件事的JS庫,但是非常強大,可以和任何測試框架結合。

testdouble是一個比較新的庫,功能和Sinon類似。但在整體設計、測試理念和特性上和Sinon 還是有些區別的,這些區別讓它在很多場景上特別適用。如果你想進一步了解這個庫,可以訪問三個鏈接

Wallaby也值得一提。雖然這是一款收費工具,但很多開發者都大力推薦使用。在IDE (支持絕大多數IDE)中就可以運行,測試是基於代碼的更改,如果執行過程中出現測試不通過的情況,會有標註在代碼邊上。

Cucumber也是在功能測試方面頗有口碑的一個庫,它的自動化測試支持上面提到的很多特性,但具體的支持方式可能不大一樣。

Cucumber 用的是BDD 的測試結構。將測試分為用Gherkin寫出期望結果的商業運營團隊和根據測試結果編寫測試代碼的開發團隊(譯者註:這個模式有點像TDD)。 Cucumber 支持多種代碼,包括我們熟悉的JS:

features/like-article.feature (gherkin語法)

Feature: A reader can share an article to social networks As a reader I want to share articles So that I can notify my friends about an article I liked Scenario: An article was opened Given I'm inside an article When I share the article Then the article should change to a "shared" state特性:读者可以在社交平台上分享一篇文章作为一名读者我想要分享文章这样我的朋友就知道我喜欢哪篇文章场景:打开一篇文章的链接假设我正在浏览这篇文章当我分享这篇文章后该文章变成"分享" 状态

features/stepdefinitions/like-article.steps.js

module.exports = function() { this.Given(/^I'm inside an article$/, function(callback) { // 功能测试代码}) this.When(/^I share the article$/, function(callback) { // 功能测试代码}) this.Then(/^the article should change to a "shared" state$/, function(callback) { // 功能测试代码}) }

很多團隊認為比起TDD,它更有用。

選擇合適單元測試和集成測試框架

開發者要做的第一件事是選擇合適的框架,找到與之配合的各種庫。如果框架官網上有推薦使用的庫,建議開發者直接採用官方的建議,除非有特殊需求。之後要在測試框架上增減都不難。

簡而言之,如果你是想踏出測試的第一步,或者想為大型項目配備足以快速上手的框架,建議使用Jest
想要靈活性高可擴展性好,那就用Mocha
想再簡單點,就用Ava
想做底層的測試,用tape

下面列出了一些常見的測試工具以及它們的特性:

Mocha應該是目前使用最廣泛的庫。和Jasmine 不同的是,它需要和第三方庫配合(通常是Enzyme 和Chai)才能有斷言、mocks、spies 的功能。

這也意味著,Mocha 的學習曲線相對較陡,但這也說明了它可以提供更好的靈活性和可擴展性。

如果想要特殊的斷言邏輯,你可以fork Chai,加上你想要的特性,然後整合到自己的Mocha環境中。當然開發者如果用的是Jasmine,也可以在自己的環境裡按需修改代碼。只是在這個場景下,Mocha 會更友好。

  • 社區-提供了各種特殊場景可用的插件或擴展
  • 可擴展性-插件、擴展還有第三方庫比如Sinon,可以提供Jasmine沒有的特性。
  • Globals -默認創建全局的測試結構,不過和Jasmine一樣,斷言、spies和mocks這些不是全局的。有人對這樣的前後不一致表示驚訝。

Jest是Facebook推薦使用的測試框架,它基於等下就會談到的Jasmine。 Facebook 重寫了大部分的功能,還加上了很多新特性。

許多開發者對Jest 的速度和方便程度的表示讚歎。

  • 性能-首先Jest基於並行測試多文件,所以在大項目中的運行速度相當快(我們在這一點上深有體會,你可以訪問這裡這裡這里這裡了解更多)。
  • UI -清晰且操作簡單
  • Ready-To-Go -有斷言、spies、mocks,和Sinon能做的事差不多。和其他庫的結合使用也很方便。
  • Globals -和Jasmine一樣,默認創建全局環境。但這一個特性確實會降低代碼靈活性和健壯性,不過大部分情況下你都會慶幸Jest 有這麼一個功能:
// "describe" 已经在全局作用域中// 不需要再这样"require" 模块了// import { describe } from 'jest' // import { describe } from 'jasmine' describe('calculator', function() { ... })
  • 快照測試- Jest快照功能由FB開發和維護,它還可以平移到別的框架上作為插件使用。
  • 更強大的模塊級mocking功能- Jest允許開發者用非常簡單的方法mock很重的庫,達到提高測試效率的目的。比如可以模擬一個promise 的resolve,而不是真的進行網絡請求。
  • 代碼覆蓋檢查-內置了一個基於Istanbul的代碼覆蓋工具,功能強大且性能高。
  • 支持性- Jest在2016年末2017年初發布了大版本,各方面都有了很大提升。大部分主流IDE 和工具都已支持
  • 開發- Jest僅僅更新被修改的文件,所以在監控模式(watch mode)下它的運行速度非常快。

Jest基於Jasmine ,那為什麼不直接用Jest呢?

首先Jasmine 歷史悠久,背後有一個很成熟的社區支持。另外Angular 更推薦Jasmine,雖然Jest 也完美支持Angular,而且很多開發者確實是用Jest 來測試他們的Angular 代碼的。

  • Ready-To-Go -所有的預備工作都已做好,開發者可以直接動手開始寫測試了。
  • Globals -核心的測試模塊都已經在全局作用域下,直接調用即可。
  • 社區- 2009年誕生,至今社區已相當成熟,累積了大量文章、工具和前人的經驗。
  • Angular - Angular官方推薦的測試框架。

Ava是一個極簡的測試框架,但也能並行地運行測試。

  • Ready-To-Go -除了spies和dubbing需要手動添加外(相當容易操作),其他功能都是現成的。要在Ava 中使用斷言可以這樣寫:
import test from 'ava' test('arrays are equal', t => { t.deepEqual([1, 2], [1, 2]) })
  • Globals -如上面代碼所示,Ava沒有定義全局變量,開發者可以更靈活地編寫代碼。
  • 簡潔-簡單的測試結構和斷言,沒有復雜的API,但也有不少高級特性。
  • 開發- Ava僅僅會更新被修改的文件,所以在監控模式下它的運行速度非常快。
  • 速度-創建一個新的Node.js進程,並行地運行測試。
  • 快照測試-基於Jest-snapshot後台運行

Tape算是本文談到的庫中最簡單的一個了。開發者只需要用node 執行一個JS 腳本,直截了當地調用API 即可。

  • 簡潔-比Ava更甚,沒有復雜的API,簡單到極致的結構和斷言。
  • Globals -沒有定義全局變量,開發者可以任意控制你的測試代碼。
  • 測試用例間沒有Shared State -為了保證模塊級的測試,和最大程度上地允許開發者控制整個測試閉環,Tape並不鼓勵開發者使用類似beforeEach這樣的函數。
  • 沒有CLI -能運行JS的環境,就能運行Tap。

UI測試

在上文也提到了,在這裡你都能找到提供UI測試的產品。專門做功能測試的工具數量有限,而且每個工具的實現方式差別頗大。一定要仔細斟酌慎重選擇工具。

簡而言之,如果你想立刻著手在多個運行環境下嘗試下功能測試,想要一個all-in-one的工具,試試TestCafe
如果你希望測試流程完整,還有強大的社區支持。 WebdriverIO是個不錯的選擇。
如果不需要測試跨瀏覽器的支持性,推薦使用Puppeteer
如果你的應用沒有復雜的界面和交互邏輯,比如一個全是表單和導航的系統。換言之,是相對較容易測試de的場景。可以使用headless瀏覽器工具,比如Casper ,高效完成測試。

Selenium ,可以控制瀏覽器模擬用戶行為。雖然這個庫並非專用於測試的,但它通過調用API 暴露了一個可以模擬用戶操作瀏覽器行為的服務器,最終實現了操作瀏覽器的目的。

Selenium 有很多使用方法,支持多種編程語言,甚至在某些工具中連代碼都不需要編寫。

根據我們的需要,Selenium服務器由Selenium WebDriver控制,Selenium WebDriver是一個介於NodeJS和操作瀏覽器的服務器之間的中間層。

Node.js <=> WebDriver <=> Selenium Server <=> FF/Chrome/IE/Safari

WebDriver 可以被引入開發者的測試框架,通過類似下面的代碼中的方法調用:

describe('login form',()=>{before(()=>{returndriver.navigate().to('http://path.to.test.app/')})it('autocompletes the name field',()=>{driver.findElement(By.css('.autocomplete')).sendKeys('John')driver.wait(until.elementLocated(By.css('.suggestion')))driver.findElement(By.css('.suggestion')).click()returndriver.findElement(By.css('.autocomplete')).getAttribute('value').then(inputValue=>{expect(inputValue).to.equal('John Doe')})})after(()=>{returndriver.quit()})})

可能對你來說WebDriver已經夠用了,但是依然有人建議可以配合插件、擴展去使用,甚至修改它的代碼,讓這個工具更加強大。

但真的把WebDriver 和別的工具一起使用以後,可能會出現冗餘代碼、debug 困難等問題。 fork後自行修改又可能會漸漸偏離主幹的發展方向

即便如此,依然有開發者傾向於不直接使用WebDriver,放我們來看看都有哪些庫這麼做吧!

Apium 提供了一個和Selenium 相似的API,用於測試站點在不同移動設備上的支持性,用到了以下工具:

如果工具是支持Selenium 或者是基於Selenium,那它也是支持Apium 的。

Protractor是一個對Selenium做了二次封裝的庫。優化了語法,內置了針對Angular的鉤子。

  • Angular -有針對Angular的特殊鉤子,雖然其他JS框架可能也有類似的功能。
  • Error reporting -良好的報錯機制。
  • 支持-支持TypeScript ,這個庫由Angular團隊開發和維護。

WebdriveIO有自己的Selenium WebDriver實現。

  • 語法-相當簡單、可讀性高。
  • 靈活性-非常簡單,甚至被用作測試,很靈活、可擴展性好的庫。
  • 社區-良好的社區氛圍,積極的開發者們貢獻了很多的插件和擴展。

Nightwatch也開發了自己的Selenium WebDriver。並提供了測試框架,和配套的服務器、斷言等等其他工具。

  • 框架-可以和其他框架一起使用。適用局部的功能測試場景。
  • 語法-可以說是幾個中最簡單、可讀性最佳的。
  • 支持-不支持TypeScript,社區文化稍弱於其他幾個框架。

TestCafe同樣基於Selenium 。在2016年底,TestCafe團隊重構了代碼,並開源項目

同時提供付費版本的工具,付費版本提供了測試記錄和客服服務。

TestCafe 是腳本注入型工具,不像Selenium 那樣作為瀏覽器插件存在。這就允許TestCafe 可以在包括移動設備在內的任意瀏覽器平台上執行,也不需要在各個平台上都安裝一遍工具。

TestCafe 更新、更JS 友好也更面向測試。它的特色之一是非常有用的錯誤報告系統,在這個系統中開發者可以追溯沒通過測試的使用路徑、一個非常有用的選擇器系統等等很多其他有用的特性。

import { Selector } from 'testcafe' ; fixture `Getting Started` . page `https://devexpress.github.io/testcafe/example` // Own testing structure test ( 'My first test' , async t => { await t . typeText ( '#developer-name' , 'John Smith' ) . click ( '#submit-button' ) . expect ( Selector ( '#article-header' ). innerText ) . eql ( 'Thank you, John Smith!' ) })

Cypress 算是TestCafe 的第一競品。它們的功能都是將測試代碼注入站點,方向也都是在尋找更智能、更便捷的測試方法。

Cypress相當年輕,2017年10月它推出public beta版本,但已經有了不少擁躉

  • 不支持跨瀏覽器-暫時只支持有界面版本的Chrome ,不過開發團隊正在擴展這個能力
  • 特性缺乏-和TestCafe相比,目前還不支持並行測試等能力。但開發團隊對此已經有了規劃。
  • 文檔-清晰明了。
  • debug工具-上手容易使用方便日誌友好。
  • 測試結構-使用Mocha的測試結構,使得開發者的UI測試代碼和其他測試代碼看起來結構相同。
describe('My First Cypress Test', function() { it("Gets, types and asserts", function() { cy.visit('https://example.cypress.io') cy.contains('type').click() // 跳转的URL 中需要包含'/commands/actions' cy.url().should('include', '/commands/actions') // 获取输入框,并输入相应内容,再验证输入合法性cy.get('.action-email') .type('[email protected]') .should('have.value', '[email protected]') }) })

Puppeteer由Google開發維護,是一個Node庫,提供API調用Headless Chrome

Chrome 59以上支持--headless參數啟動Headless Chrome 。 Headless Chrome 提供了一系列接口允許開發者通過代碼控制瀏覽器,Puppeteer 就是這麼一個工具。

值得一提的是,2017年底Firefox也發布了自己的Headless版本

除了Puppeteer 外還有很多工具支持Headless Chrome 和Headless Firefox,比如TestCafe 和Karma。

  • 相比之下Puppeteer尚且年輕,但有強大的社區和開發團隊在支持著。
  • 使用原生的最新版Chrome內核,比起使用舊版Webkit內核的Phantom更快更可靠。
  • Headless Chrome最大的問題是它不支持安裝擴展(比如flash),而且目前也沒有這方面規劃

Phantom實現了一個headless Webkit內核的瀏覽器(無界面可編程的瀏覽器) ,這種瀏覽器介於真正的瀏覽器和JSDom之間,它的穩定性和速度自然也是在兩者之間。

在筆者撰寫這篇文章的時候(譯者註:2017年4月份)Phantom 風頭正盛。但是自從Google把headless作為特性直接加入Chrome後。 PhantomJS之父和主要維護者Vitaliy Slobodin便聲明他將不再維護這個工具了。

使用Phantom 而不用Puppeteer 的理由可能是什麼呢:

  • Phantom相當完備,有足夠多的資料可供參考。
  • 不少測試工具比如CasperJS用到了Phantom。
  • 舊的Webkit內核可以模擬早期Chrome
  • Headless Chrome不同的是,Phantom支持擴展

作為一個UI 測試工具,Nightmare 的語法可以說是相當簡單了。

基於和Phantom類似的Electron ,但它用的是新版的Chromium內核,持續開發和維護。 Electron 主要功能是允許開發者使用JavaScript、HTML 和CSS 開發出強大的跨平台應用。

Nightmare團隊也在討論驗在Headless Chrome上的效果。它的代碼看起來和Phantom 很像:

nightmare-example.js:

yield Nightmare() .goto('http://yahoo.com') .type('input[title="Search"]', 'github nightmare') .click('.searchsubmit')

raw-phantom.js:

phantom.create(function (ph) { ph.createPage(function (page) { page.open('http://yahoo.com', function (status) { page.evaluate( function () { var el = document.querySelector('input[title="Search"]') el.value = 'github nightmare' }, function (result) { page.evaluate( function () { var el = document.querySelector('.searchsubmit') var event = document.createEvent('MouseEvent') event.initEvent('click', true, false) el.dispatchEvent(event) }, function (result) { ph.exit() } ) // page.evaluate } ) // page.evaluate }) // page.open }) // ph.createPage }) // phantom.create

Casper基於PhantomSlimerJS (和Phantom類似,但用了火狐的Gecko內核),提供了導航、腳本、測試,降低了編寫腳本的難度。

開發者已經使用了未正式發布的Slimer 一段時間,它在2017 年底發布了beta 版,基於Headless Firefox。開發團隊正致力於開發第一個正式穩定版。

Casper 有可能在2.0 版本中從Phantom 移植到Puppeteer,屆時將同時支持Headless Chrome 和Headless Firefox。值得大家期待。

和上文中提到的CucumberJS類似, Codecept提供了另一套語法用於描述用戶和產品的交互,基於這套語法編寫出的代碼如下:

Scenario('login with generated password',async(I)=>{I.fillField('email','[email protected]');I.click('Generate Password');constpassword=awaitI.grabTextFrom('#password');I.click('Login');I.fillField('email','[email protected]');I.fillField('password',password);I.click('Log in!');I.see('Hello, Miles');});

除了Codecept外,剛剛我們說到的WebDriverIO , Protractor , Nightmare , Appium , Puppeteer也都支持這種語法。

結論

在這篇簡短的測試指南中,我們可以看到最近的測試趨勢和JS 社區對「測試」的態度,希望看到這裡的你能更輕鬆地上手測試你的產品。

要知道還有很多有用的工具或測試方案是這篇只能沒有講到的,可能其中有些和上面說到的工具有關,也可能會是完全不同的東西。

最後,要決定使用哪一種測試方案應該從產品本身出發,另外開發者還需要從社區活躍度、和項目的適配程度以及測試框架的特性等角度仔細考慮、比較不同的方案。選擇出最適合自己項目的。
接下去就是反復不斷地開發、測試、開發、測試... 🙂

測試愉快:)

謝謝:)

推薦閱讀

首推

概覽

spies和stubs

測試框架間的比較

Jest

Ava

Tape

UI Test

TestCafe

Cypress

What do you think?

Written by marketer

2018 我所了解的Vue 知識大全(一)

WebVR開發教程——Web Audio實現3D音效