前端進階-編寫測試程式碼

moduzhang發表於2018-12-04

Sublime Text 快捷鍵

Gulp

Gulp 是基於 node.js 的一個前端自動化構建工具,開發這可以使用它構建自動化工作流程(前端整合開發環境)。 使用 gulp 你可以簡化工作量,讓你把重點放在功能的開發上,從而提高你的開發效率和工作質量。

安裝 Gulp

在安裝 gulp 之前,需要先安裝 Node.jsNPM。我們可以通過訪問 Nodes官網 並下載最新版的 NodeJS,來下載和安裝 NodeJS 和 NPM。然後安裝 Gulp

在這裡插入圖片描述

每個 Gulp 專案都以一個 Gulp 檔案開始,此檔案位於專案的根目錄中 gulpfile.js,並規定你在執行 Gulp 時應執行的所有任務。由於 Gulp 檔案本質上是一個 node.js 的指令碼, 因此我們首先需要將 Gulp 作為依賴項,然後設定一個預設的任務。

var gulp = require('gulp');
gulp.task('default', function() {
	console.log("Hello, Gulp!");
});

在這裡插入圖片描述

Grunt 任務和 Gulp 流

在這裡插入圖片描述

Sass

Sass 是一款強化 CSS 的輔助工具,它在 CSS 語法的基礎上增加了變數 (variables)、巢狀 (nested rules)、混合 (mixins)、匯入 (inline imports) 等高階功能,這些擴充令 CSS 更加強大與優雅。

當我們在 Sass 中編寫樣式表時,可以擺脫很多 CSS 的煩惱,並將其編譯為純 CSS。不要手動為每個瀏覽器設定 CSS 屬性,使用自動字首完成自動設定。

Jasmine

測試各類同步及非同步功能,JavaScript 沒有內建測試模組,我們需要使用庫來獲取此類功能。Jasmine 2.2

<!--引入 jasmine 庫-->
<script src="lib/jasmine-2.2.0/jasmine.js"></script>
<script src="lib/jasmine-2.2.0/jasmine-html.js"></script>
<script src="lib/jasmine-2.2.0/boot.js"></script>
<!--引入原始檔(應用程式碼)-->
<script src="src/Player.js"></script>
<script src="src/Song.js"></script>
<!--規格說明(測試檔案)-->
<script src="spec/SpecHelper.js"></script>
<script src="spec/PlayerSpec.js"></script>

describe 是用於內容組織的工具,與縮排的作用類似。 describeit 函式的使用創造了用於組織資訊的提綱。it 用於指明程式規格(spec),它是封裝測試程式碼的容器,幫助明確程式具體特性。如果所有 spec 內容返回 true 則該 spec 通過,其中任何一項出現問題都會造成測試失敗。it 定義了這一測試的邊界,describe 確定了一套測試內容,由一組相關 spec 組成。這套測例定名為 Player 表示其內容都與播放器相關。

// describe 函式-呼叫標記為黑色
describe("Player", function() {
  var player;
  var song;
  beforeEach(function() {
    palyer = new Player();
    song = new Song();		
  });
  it("should be able to play a Song", function() {
    player.paly(song);
    // expect 函式,後面跟一個匹配器-呼叫標記為綠色
    expect(player.currentlyPlayingSong).toEqual(song);

   // ...
    expect(player).toBePlaying(song);
  });
  // ...
})

不同的組織測試的方法並沒有對錯之分,它取決於個人喜好。

編寫測試

expect(add(0.1, 0.2)).toBe(0.3);
// 其執行效果如下
add(0.1, 0.2) === 0.3 // 表示式返回 true 測試通過
// 如果想對結果取反,在匹配器前加 not
expect(add(0.1, 0.2).not.tobe(0.1));

每個測試都是從 expect 呼叫開始,可以把它視為測試的出發點,開啟了測試流程。expect 函式接收一個通常稱為 actual 的單變數例項,add(0.1, 0.2) 是我們的測試物件。接下來告訴測試框架要對結果進行怎樣的比較,比較函式又稱為匹配器(matcher),是跟在 expect 之後的方法。Jasmine 有大量匹配器可用,也可以自定義它們。toBe 在語義上表示嚴格等於,最後我們把期望值傳到匹配器。

紅綠重構(refactor)週期

先寫出測試,開始時候沒有進行實現,所有測試點均失敗。之後開始寫程式碼以通過測試,測試保證你能安全地進行程式碼重構,為系統新增新特性。地址本示例如下:

先建立兩個檔案 AddressBookSpec.jsAddressBook.js。然後更新 html 來執行它們:

<!--引入 jasmine 庫-->
<script src="lib/jasmine-2.2.0/jasmine.js"></script>
<script src="lib/jasmine-2.2.0/jasmine-html.js"></script>
<script src="lib/jasmine-2.2.0/boot.js"></script>
<!--引入原始檔(應用程式碼)-->
<script src="src/AddressBook.js"></script>
<!--規格說明(測試檔案)-->
<script src="spec/AddressBookSpec.js"></script>

describe 建立 AddressBook 測試組,在其中用 it 新增內容要求應用能夠新增聯絡人,採用物件導向方法處理這個問題,新建一個 AddressBook物件,向其中加入聯絡人,需要應用中有 addContact 方法,向方法中傳入物件作為引數,下一步是確定合適的方法,檢測聯絡人資訊是否成功加入。如果獲得地址本的第一個聯絡人應該與剛加入的聯絡人相同。

describe("Address Book", function() {
  it("should be able to add a contact", function() {
    var addressBook = new AddressBook(),
        thisContact = new Contact();

    addressBook.addContact(thisContact);
    expect(addressBook.getContact(0)).tobe(thisContact);
  });
});
function AddressBook() {
  this.contacts = [];
}

AddressBook.prototype.addContact = function(contact) {
  this.contacts.push(contact);
}

AddressBook.prototype.getContact = function(index) {
  return this.contacts[index];
}

移除冗餘程式碼

describe("Address Book", function() {
  it("should be able to add a contact", function() {
    var addressBook = new AddressBook(),
        thisContact = new Contact();

    addressBook.addContact(thisContact);
    expect(addressBook.getContact(0)).tobe(thisContact);
  });

  it("should be able to delete a contact", function() {
    var addressBook = new AddressBook(),
        thisContact = new Contact();

    addressBook.addContact(thisContact);
    addressBook.deleteContact(0);
    expect(addressBook.getContact(0)).not.toBeDefined();
  });
});

上述程式碼中,我們發現一份冗餘程式碼,在每份規格說明中都要重設 Contact 和 AddressBook。一方面這讓我們的程式碼耦合度很低,另一方面我們在低效手動操作。Jasmine 提供了一個函式幫助我們在每個測例前執行特定程式碼,函式名為 beforeEach

var addressBook,
    thisContact;
    
beforeEach(function() {
  addressBook = new AddressBook(),
  thisContact = new Contact();
});

測試非同步程式碼

我們需要在函式結束時傳遞必要資訊。

AddressBook.prototype.getInitialContacts = function(cb) {
  var self = this;
  setTimeOut(function() {
    self.initialComplete = true;
    if (cb) {
      return cb();
    }
  }, 3);
}

在函式執行結束後,可以看到建構函式中將初值設定為 false。

describe("Async Address Book", function() {
  it("should grab initial contacts", function() {
    var addressBook = new AddressBook();
    address.getInitialContacts();
    expect(addressBook.initialComplete).toBe(true);
  });
});

對非同步函式的正確測試

describe("Async Address Book", function() {
  var addressBook = new AddressBook();

  beforeEach(function(done) {
    addressBook.getInitialContacts(function() {
      done();
    });
  });

  if("should grab initial contacts", function(done) {
    expect(addressBook.initialComplete).toBe(true);
    done();
  });
});

加上 beforeEach 函式,在非同步函式回撥內容中加入 done(),它會通知測試框架。把 done 作為引數傳入規格說明,測試結束再呼叫 done,包括非同步測試全部通過。

相關文章