前端單元測試總結及測試工具介紹

釋懷發表於2015-06-26
1.為什麼需要單元測試
  • 正確性:測試可以驗證程式碼的正確性,在上線前做到心裡有底
  • 自動化:當然手工也可以測試,通過console可以列印出內部資訊,但是這是一次性的事情,下次測試還需要從頭來過,效率不能得到保證。通過編寫測試用例,可以做到一次編寫,多次執行
  • 解釋性:測試用例用於測試介面、模組的重要性,那麼在測試用例中就會涉及如何使用這些API。其他開發人員如果要使用這些API,那閱讀測試用例是一種很好地途徑,有時比文件說明更清晰
  • 驅動開發,指導設計:程式碼被測試的前提是程式碼本身的可測試性,那麼要保證程式碼的可測試性,就需要在開發中注意API的設計,TDD將測試前移就是起到這麼一個作用
  • 保證重構:網際網路行業產品迭代速度很快,迭代後必然存在程式碼重構的過程,那怎麼才能保證重構後程式碼的質量呢?有測試用例做後盾,就可以大膽的進行重構
2.前端相關的單元測試技術
2.1 測試框架
目前,前端的測試框架很多,像QUnit、jasmine、mocha、jest、intern等框架,這些框架各有特點,簡單描述下,感興趣的可以具體研究:
  • Qunit: 該框架誕生之初是為了jquery的單元測試,後來獨立出來不再依賴於jquery本身,但是其身上還是脫離不開jquery的影子
  • jasmine: Behavior-Drive development(BDD)風格的測試框架,在業內較為流行,功能很全面,自帶asssert、mock功能
  • mocha: node社群大神tj的作品,可以在node和browser端使用,具有很強的靈活性,可以選擇自己喜歡的斷言庫,選擇測試結果的report
  • intern: 看官方介紹該測試框架功能極其全面,似乎囊括了業內跟測試相關的所有功能
2.2 斷言庫
  • chai:應該是目前組流行的斷言庫了,支援TDD(assert)、BDD(expect、should)兩個風格的斷言庫
var chai = require('chai'); 

var assert = chai.assert; // typef assert === 'object'
chai.should(); // 對Obejct.prototype進行擴充

  •  should.js: TJ的另外一個開源貢獻
  • expect.js:BDD風格的另外一個斷言庫,基於should.js,是mini版的BDD庫
  • assert(node自帶核心模組): 可以在node中使用的斷言模組
  •  
    2.3 mock庫

    先來說說為什麼需要mock吧:需要測試的單元依賴於外部的模組,而這些依賴的模組具有一些特點,例如不能控制、實現成本較高、操作危險等原因,不能直接使用依賴的模組,這樣情況下就需要對其進行mock,也就是偽造依賴的模組。例如在使用XMLHttpRequest時,需要模擬http statusCode為404的情況,這種情況實際很難發生,必然要通過mock來實現測試。
    • sinon.js: 目前使用最多的mock庫,將其分為spies、stub、fake XMLHttpRequest、Fake server、Fake time幾種,根據不同的場景進行選擇。
    2.4 test runner
    • karma: 設定測試需要的框架、環境、原始檔、測試檔案等,配置完後,就可以輕鬆地執行測試。
    3.單元測試技術的實現原理
    • 測試框架:判斷內部是否存在異常,存在則console出對應的text資訊
    • 斷言庫:當actual值與expect值不一樣時,就丟擲異常,供外部測試框架檢測到,這就是為什麼有些測試框架可以自由選擇斷言庫的原因,只要可以丟擲異常,外部測試框架就可以工作。
    • mock函式:建立一個新的函式,用這個函式來取代原來的函式,同時在這個新函式上新增一些額外的屬性,例如called、calledWithArguments等資訊
    function describe (text, fn) {
        try {
            fn.apply(...);
        } catch(e) {
            assert(text)
        }
    }
    
    function fn () {
        while (...) {
            beforeEach();   
            it(text, function () {
                assert();
            }); 
            afterEach();
        }
    }
    
    function it(text, fn) {
        ...
        fn(text)
        ...
    }
    
    function assert (expect, actual) {
        if (expect not equla actual ) {
            throw new Error(text);
        }
    }
    function fn () {
        ...
    }
    
    function spy(cb) {
        var proxy = function () {
            ...
        }
        proxy.called = false;
        proxy.returnValue = '...';
        ...
        return proxy;
    } 
    
    var proxy = spy(fn); // 得到一個mock函式

    4.如何寫單元測試用例
    4.1原則
    • 測試程式碼時,只考慮測試,不考慮內部實現
    • 資料儘量模擬現實,越靠近現實越好
    • 充分考慮資料的邊界條件
    • 對重點、複雜、核心程式碼,重點測試
    • 利用AOP(beforeEach、afterEach),減少測試程式碼數量,避免無用功能
    • 測試、功能開發相結合,有利於設計和程式碼重構
    4.2 TDD
    一句話簡單來說,就是先寫測試,後寫功能實現。TDD的目的是通過測試用例來指引實際的功能開發,讓開發人員首先站在全域性的視角來看待需求。具體定義可以檢視維基;
    就個人而言,TDD不是一個技術,而是一種開發的指導思想。在目前網際網路的開發環境下,業務開發很難做到TDD開發,一是因為需要更多時間編寫單元測試用例;二是要求非常瞭解業務需求;三是要求開發人員有很強的程式碼設計能力。但是當我們寫元件、工具方法、類庫的時候,TDD就可以得到很好地使用。
    4.3 BDD
    行為驅動開發要求更多人員參與到軟體的開發中來,鼓勵開發者、QA、相關業務人員相互協作。BDD是由商業價值來驅動,通過使用者介面(例如GUI)理解應用程式。詳見維基.
    相關閱讀
    評論(1)

    相關文章