karma 測試框架的前世今生

引言

在前端測試框架的學習中瞭解到了karma的強大,搜索得到淘寶前端團隊的這片講述karma特點及原理的分享,非常不錯。轉載分享至此,
原文鏈接:http://taobaofed.org/blog/2016/01/08/karma-origin/
原作者: 亦才[2016-01-08]

正文

背景

JavaScript 作爲 web 端使用最廣泛的編程語言,它是動態語言,缺乏靜態類型檢查,所以在代碼編譯期間,很難發現像變量名寫錯,調用不存在的方法等錯誤,除非在運行時才能暴露出來,所以非常有必要有一個測試工具來驗證你的代碼。

karma 就是在這樣的背景下產生的, 它是一個 runner , 旨在幫助開發者簡單而又快速的進行自動化單元測試, 目前已經用在很多大型的項目, google 和 youtube 這些公司都在用它, 在 npm 官方網站上, 它也是一個比較流行的 npm 模塊。

下面會講述 karma 的由來, 以及現有解決方案的比較, 最後會談及 karma 的設計與實現。


爲什麼需要測試

下面從兩方面來說明

從語言角度

  • JavaScript 是動態語言, 缺少類型檢查,編譯期間無法定位到錯誤
  • JavaScript 宿主的兼容性問題, 比如 DOM 操作在不同瀏覽器上的表現

從工程角度


  • TDD 被證明是有效的軟件編寫原則,它能覆蓋更多的功能接口
  • 測試可以快速反饋你的功能輸出,驗證你的想法
  • 測試可以保證代碼重構的安全性,沒有一成不變的代碼,測試用例能給你多變的代碼結構一個定心丸。
  • 測試用例其實可以看成代碼 API 使用文檔,而且還是定時更新的,對 API 的使用者有很大的幫助
  • 易於測試的代碼,說明是一個好的設計,這裏可以稍微說明一下

做單元測試之前,肯定要實例化一個東西,假如這個東西有很多依賴的話,這個測試構造過程將會非常耗時,會影響你的測試效率,怎麼辦呢?要依賴分離,一個類儘量保證功能單一,比如視圖與功能分離,這樣的話,你的代碼也便於維護和理解。


目前我們面臨的現狀

相信現在前端開發者對測試都不是很依賴,因爲缺少好用的工具,甚至都不會去寫測試。

其它語言像 Java, Python ,C#, 測試工具基本都會集成到 IDE 中,基本都能馬上看到測試結果,而且像 C++ 這種編譯性語言,在編譯期間基本都會找到代碼裏的錯誤

當我們把焦點放在前端開發中的 JavaScript 上來時,上面語言的一些優點完全沒有了,關鍵就是缺少一個流程控制的測試工具。


karma 的目標

  • 在真實環境中測試
  • 支持遠程控制
  • 執行速度快
  • 可以跟第三方 IDE 進行交互
  • 支持 ci 服務
  • 高擴展性,支持插件開發
  • 支持調試
  • 已有的解決方案

已有的解決方案

Selenium

Selenium 是一個完整的測試工具,它是一個比較成熟的古老測試工具之一,它比較適合用來做高級測試,比如 e2e 測試。

它的核心思想就是代理注入,Selenium 通過代理打開瀏覽器 URL, 有一個後臺的 server 在監聽,當有請求時,會往瀏覽器中注入腳本,之後就通過這個腳本來跟 server 端通訊。

開發者可以使用 Selenium 客戶端 API 來編寫單測, 比如跳轉 URL或者單擊按鈕

Selenium 客戶端有多種實現,比如 Java,Python,Ruby,開發者可以使用自己熟悉的語言來編寫單例


WebDriver/Selenium 2

WebDriver 是基於 HTTP 協議來跟瀏覽器通訊的,Selenium 2 實現了完整的 WebDriver 協議

WebDriver 可以調用更底層的 DOM API, 像 Safari 不支持這種協議的話,就採用 Selenium 1 那種代理注入的方式通訊


Mocha

Mocha 既是測試框架,也是一個測試 runner ,它主要用在 Node.js裏的單元測試,當然也可以用在瀏覽器端,不過得手動配置各種適配腳本。


JsTestDriver

JsTestDriver 是直接在瀏覽器裏執行的,所以可以直接調用 DOM 和瀏覽器端 API, 不過先得開啓一個服務端程序, 它的核心是一個 runner 程序,當運行單測時,runner 會通知服務端來更新瀏覽器端,比如重新加載 js 腳本,返回測試結果到服務端。


HTML Runners

大部分的測試框架,像 Jasmine 和 QUnit 都包含一個 HTML runner, 開發者要自己維護加載的前端資源, 手動刷新頁面, 測試結果以 HTML 形式顯示


對比

下面主要比較下,上面這些工具框架的優缺點

  • Selenium 和 WebDriver 不支持直接訪問 JavaScript 代碼能力,只是在瀏覽器裏執行,通過各種命令來操作,它的最大優點就是可以訪問原生 DOM 事件,這個是 JavaScript 裏是很難模擬的,所以這非常適合做 e2e 測試,WebDriver 基本是 e2e 測試的標準了。

  • HTML runners 僅僅是一個測試框架,可以在瀏覽器端執行然後輸出結果,它缺少一種流程管理機制,而且跟第三方編輯器以及 CI Servers 都不能進行通訊

  • Mocha 提供了一種很好的測試控制,但是它比較適合用在 Node.js 環境裏,瀏覽端運行的話,也會產生 HTML runners 類似的問題

  • JsTestDriver 是最接近完美的測試工具了,它可以直接訪問瀏覽器端 API , 而且也有管理工具,可以在命令行裏進行控制,但是它有兩個主要的問題

    • 代碼設計有缺陷,不穩定以及不容易擴展
    • 沒有文件監聽,以及文件預處理功能,這些都很影響使用的無縫接入
  • Karma 就是對 JsTestDriver 的改進,提供了文件監聽以及預處理能力, 文件監聽很重要,它方便與各種編輯器接入, 減少用戶運行單測的成本,直接保存文件就可以了, 下面是一張各種工具的對比圖

這裏寫圖片描述


Karma 設計分析

karma 設計目標主要有下面四點:

  • 高效
  • 擴展性
  • 運行在真實設備
  • 無縫的使用流程

karma 是一個典型的 C/S 程序,包含 client 和 server ,通訊方式基於 Http ,通常情況下,客戶端和服務端基本都運行在開發者本地機器上。

一個服務端實例對應一個項目,假如想同時運行多個項目,得同時開啓多個服務端實例。


Server

Server 是框架的主要組成部分之一,它內部保存了所有的程序運行狀態,比如 client 連接,當前運行的單測文件,根據這些數據狀態,它提供了下面幾個功能, 下圖是 server 的結構
這裏寫圖片描述


karma_server

  • 監聽文件
  • 與 client 進行通訊
  • 向開發者輸出測試結果
  • 提供 client 端所需的資源文件

注意:連接 server 的 client 瀏覽器有多種類型,比如 PC,iphone,TV 端等

Server 端運行在開發者機器上,根據測試配置文件,它能快速的訪問本地測試文件


下面主要說下 Server 端的 4 個組成部分:

1.Manager

Manager 的主要責任就是跟 client 進行通訊,比如廣播信號通知 client 開始測試以及收集 client 返回的測試結果

Manager 內部維護了兩個數據模型:

  • 一個用來保存所有連接的 client 實例(包含瀏覽器名稱和版本號,以及一個標明它是閒置還是當前正在運行的狀態)
  • 一個用來保存當前測試狀態(多少測試成功和多少測試失敗)
    作爲通訊網關,它會利用這兩種數據模型去通知服務端其它組成部分,比如測試完成之後,通知輸出結果展示。

2.Web Server

基於 connect 的一個 server , 主要是提供訪問本地靜態資源用的,這裏的資源包含:JS 測試框架,斷言庫,測試用例以及它的依賴等。


3.Reporter

利用上面的測試數據模型,reporter 展示輸出結果,輸出端包含本地命令行,文件或者一個 ci server。


4.File System Watcher

watcher 主要是監聽本地文件改變,內部維護了一個數據模型,包含所有測試相關的文件,它能保證 Web Server拉取的靜態資源都是最新的,同時也能保證文件訪問成本以及網絡成本,永遠只加載修改的文件。


Client

Client 是測試文件真正運行的地方,比如一個 PC,iphone,tablet 端的瀏覽器,通常情況下跟 server 是同一個物理機,當然也可以運行在不同的機器,通過 HTTP 來通訊

多個 Client 可以與一個 Server 進行通訊

1.Manager

這裏主要是跟 server 進行消息通訊,以及與其它 client 組成部分進行交互,比如測試框架 mocha


2.Testing Framework

測試框架不是系統的一部分,karma 靈活支持第三方測試框架,以插件的形式接入。


3.Tests and Code under Test

這裏包含用戶所有的測試相關文件,它是通過 web-server 模塊來獲取,測試文件由 test framework 來執行。


Communication Protocal

主要描述 server 與 client 的通訊時機

1.Client to Server Messages

  • 當 client 連接以後,會向 server 發送一個唯一標識 id , 以及它的 name 和 version
  • 當一個單測文件測試完成之後,會向 server 發送一條消息,消息會包含成功或者失敗等
  • 當所有單測文件測試完成之後,會向 server 發送一條消息
  • 當有 error 產生時,不管是在 client 啓動時還是測試代碼運行時,都會向 server 發送一條消息

2.Server to Client Messages

  • 當 server 決定開始運行單測時,會向所有 client 發送一條消息

Karma 實現分析

Karma 是用 JavaScript 實現的, server 端運行在 Node.js 環境下, client 運行在瀏覽器環境下, 爲什麼選擇使用 Node.js ,下面是作者的幾點看法:

  • 支持多個平臺
  • 用一種編程語言來解決 server 和 client 端的實現
  • 它是一個多產的平臺( npm 上有很多不錯的模塊)
  • 想深入探索異步編程

上面囉嗦了這麼多,終於要說到正題了,下面主要說下 server 和 client 的核心實現


Server

這部分主要說下 server 的核心實現,下圖中實線代表直接方法調用,虛線代表通過事件通訊

如圖 server 的組成部分
karma_impl_server


1.File System Watcher

文件監聽是基於 chokidar npm 模塊來實現的,它是對 Node.js 底層 API 的封裝(fs.watch 和 fs.watchFile), 系統啓動時,chokidar 根據配置文件會對 glob 形式的文件字符串進行監聽

chokidar 屏蔽了不同系統之間的差異問題,而且提供了一些 Node.js 底層 API 不支持的功能,比如對整個文件目錄進行監聽


2.File System Model

文件模型主要是爲了提高訪問本地文件以及網絡文件的性能, 它包含跟測試相關文件的原數據描述,這些文件是通過 karma.conf.js 配置文件裏的 files 字段, glob 形式的文件字符串, 比如 js/src/.js , test/*/*.js ,來綁定監聽的。

當你添加,刪除,修改文件,都會觸發文件模型裏定義的 file_list_modified 事件,該事件有兩個觀察者

  • web server, 接收到最新的文件數據列表
  • manager , 刷新瀏覽器,重新執行測試文件

內部主要有兩個類,FileList,File, 前者包含多個 File 實例, File 單個實例會保存測試文件的一些基本數據,比如路徑等。

如下圖描述
karma_impl_fs


3.Web Server

這塊是基於 connect 來實現的,然後系統提供不同的 handlers 來解決不同的請求,主要有下面四種 handle :

  • karma client files(跟 server 端通訊的 JS 文件)
  • testing framework and adapter
  • source and test files
  • proxy

當 web server 請種各種文件時,需要跟上面的 fs model 進行交互,拿到具體的文件原數據之後,做最合適的請求響應


4.Reporter

reporter 會接收來自 client 端的請求,然後展示相對應的單測成功或者失敗消息,這塊 karma 支持以插件的形式擴展自己想要的 reporter 效果。


Client

client 是單測最終運行的地方,類似一個 web app , 跟 server 端通訊利用 socket.io, 執行單測在一個獨立的 iframe 中。下面是它的結構圖
karma_impl_client


1.Manager

client manager 是對socket.io調用的一個簡單的包裝,它提供了一些 API , 給 test framework adapter 來調用, 方便與 server 端通訊, 下面是它的一些 API 列表
karma_impl_client_API

manager 運行在 HTML 裏的主窗口內,它提供的 API 會暴露給 iframe 裏的 test framework adapter 來調用


2.Iframe

client 包含一個獨立的 iframe , 它會加載框架自帶的 context.html 文件,頭部會引用一堆以 script 標籤的資源文件,這些文件來源於你的配置文件,通過 web server 裏的 handle 來構建完整的 context.html 文件內容。

用獨立的 iframe 來執行單測有很多好處,不會污染全局的 window 對象, 方便文件更新後, 重新執行單測


3.Adapter

這是第三方測試框架對應的 karma 適配器,本身不是框架的一部分,是以插件的形式加載到 HTML 裏的, 這樣就可以保證, 無論什麼測試框架,karma 都可以無縫接入, 想要系統適應不同框架的問題,就得提供一套約束出來,想要開發一個適配器,至少得實現 karma.start 方法, 這個方法最終會被 manager 來調來, 它是執行本地單測的入口方法,下面是 Jasmine 測試框架的適配器代碼
karma_impl_client_adapter


Communication Between Server and Client

client 和 server 端通訊採用 socket.io

  • client 端會發送這些消息

karma_impl_client_message_c

  • server 端會發送這些消息

karma_impl_client_message_s


Dependency Injection Framework

karma 框架是基於 di 開發的,di 這個 npm 模塊也是 karma 作者自己開發的,是一個依賴注入庫,這裏主要應用了設計模式裏的 ioc 原則, 簡單的說, 就是函數的參數不是手動實例,而是通過依賴注入進去的,這樣可以極大提高系統的擴展性以及靈活性, 而且代碼本身更容易測試了,推薦大家在開發複雜系統時,使用這種設計思想。


爲什麼寫這篇文章?

  • 學習 karma 框架的設計思想
  • 增加前端同學對測試的理解與認識
  • 希望大家開始編寫前端單元測試

相關引用


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章