Angular 測試小窺

阿狸不歌發表於2018-04-15

為什麼要做測試?

一堆程式碼在其生命週期內一般都會被多次修改。如果某人告訴你他的程式碼從建立起就從未修改過,那麼一般來說有幾種可能:1、這段程式碼就是寫著玩的,不會用在生產環境中;2、他的專案後來被取消了;3、程式碼是從別的地方copy & paste過來的。

只要程式被改過,不管在被改之前有多麼的穩定,你都無法100%的保證在被改之後,它依然能夠穩定的執行,所以你只要改過程式後一定要測試了再上線,否則就等著PM來收拾吧?

測試主要分為兩種,每一個程式設計師肯定都會第一種:手動測試——點啊、點啊、點啊……∞。第二種嘛,自然就是自動測試啦,只要點一下?️,等著看報告就可以了。哪種更好,自然不言而喻。

在每個程式設計師都會的第一類測試上,我們的流程一般是這樣的:寫一大堆程式碼然後編譯、點Run▶️;看看結果是不是符合預期,如果不符合就再修改再編譯再重複“測試”。在此過程中,測試是在程式碼寫完之後的事情,總是一件在coding後面才做的事情。實際上,我們的這種第一類測試還有個有趣的名稱——debug(抓蟲子?),其實找bug本身也是風險和不確定的來源,你“搞定”一個bug的同時可能還會導致另一個bug,甚至是一堆bug?。


JavaScript測試驅動開發

什麼是 測試驅動開發 Test-Driving Development (TDD)?

TDD是敏捷開發(XP)中的一項核心實踐和技術(當然,你不搞敏捷那一套也是可以用TDD的),同時也是一種設計方法論。簡單地說,就是在開發“功能”程式碼之前,先編寫單元測試(Unit Test)用例程式碼。在沒有單元測試的前提下不可以寫功能(產品)程式碼,有什麼測試程式碼再確定需要編寫什麼產品程式碼。TDD的核心思路就是通過測試來推動開發的進行,但是,與傳統的開發、測試分離不同,TDD已經不再是單純的測試工作,而是一整套的開發思想,是把需求分析、設計、質量控制量化的過程。

測試自動化是TDD的關鍵。在TDD過程中,我們要首先寫自動化單元測試程式碼,再寫緊跟滿足這些測試的功能(產品)程式碼。單元測試程式碼會隨著功能(產品)程式碼的擴大不斷增長,單元測試程式碼雖然不跑在生產環境裡,但實際上它也是整個專案裡的重要資產(等到專案重構的時候,你就會感覺到測試程式碼的價值了)!當我們每次微小改動程式碼時,相應的測試都會執行一次。這樣不僅保證了新程式碼是可工作的,同時也保證了已存在的程式碼與新的改動相容。


Angular 測試

具體到Angular (2.x及以上),我之所以喜歡用Angular有一個很重要的原因就是ng提供的工具鏈很完整,當我們用Angular CLI 的 ng new 指令新建一個專案的時候,自動生成的基礎結構裡已經包含了 Jasmine測試框架 、 karma測試執行器、protractor端到端測試工具,用不著你再自己去費力折騰了。

Angular 專案的 devDependencies

利用Jasmine,我們可以設定程式碼在呼叫後的預期結果。

先看一個簡單的例子,我們定一個“計算器(Calculator)”物件有一個“加法(sum)”函式。想確定1加1的結果為2,就可以用一個測試來表達。使用以下程式碼:

describe('計算器測試', () => {  
    it(' 1 + 1 = 2嗎?', () => {  
        var calc = new Calculator();  
        expect(calc.sum(1, 1)).toEqual(2);
    };  
};  

我們可以看出來,使用Jasmine一個優點就是程式碼簡單易讀——測試用例總是以describe開始的,它代表一組相似的測試用例,然後具體的測試用例以it開始。測試通常用如上的多個describe塊和it塊組成,expect則是斷言表示式。

  1. 每個測試檔案中可以包含多個describe
  2. 每個describe中可以包含多個it
  3. 每個it中可以包含多個expect
  4. describe可巢狀使用

有了上述Jasmine的小基礎,我們再來看一個Angular Service的測試例子就容易理解多了

// 可以直接用的Jasmine測試程式碼
describe('ValueService', () => {
  let service: ValueService;
  beforeEach(() => { service = new ValueService(); });

  it('#getValue 函式必須返回 real value', () => {
       expect(service.getValue()).toBe('real value');
  });

  it('#getObservableValue 必須從 observable 返回值 observable value',
    (done: DoneFn) => {
    service.getObservableValue().subscribe(value => {
      expect(value).toBe('observable value');
      done();
    });
  });

  it('#getPromiseValue 必須從promise返回值',
    (done: DoneFn) => {
    service.getPromiseValue().then(value => {
      expect(value).toBe('promise value');
      done();
    });
  });
});

一個Angular程式主要由 component、service、pipe構成,它們都可以用與上述方式類似的形式進行測試。更完備的教程可以參考Angular官方中文網站 https://angular.cn/guide/testing


小結

JavaScript測試驅動開發》帶給JS程式設計師的既有TDD的理念,也有覆蓋從後端到前端的Node.JS、Express、jQuery、Angular的測試實戰程式碼,只要我們擁有TDD的思想,不難把它們應用到React和Vue的開發當中去。

相關文章