0%

[week 3] 初探 Jest:如何測試程式?

學習目標:

 了解為什麼我們需要 unit test
 了解什麼是 unit test
 了解如何寫 unit test
 了解如何測試一個 function

測試程式的作用是「模擬外部如何使用目標程式,驗證目標程式的行為是否符合預期」。

利用 console.log() 測試

在前面幾個章節,我們通常會使用 console.log(),來測幾個範例確認是否正確。也需要考慮到邊界條件(edge case)來進行測試。

// 以測試 `repeat` 函式為例:

function repeat(str, times) {
  let result = ''
  for (let i = 0; i < times; i += 1) {
    result += str
  }
  return result
}

console.log(repeat('a', 5));          // 印出 aaaaa
console.log(repeat('z!Z!Z!!', 2));    // 印出 z!Z!Z!!z!Z!Z!!
console.log(repeat('', 3));           // 印出空字串
console.log(repeat('abc', 0));        // 印出空字串

接著優化測試資料:直接判斷函式執行結果是否正確。

// 優化測試資料:

console.log(repeat('a', 5) === 'aaaaa');
console.log(repeat('z!Z!Z!!', 2) === 'z!Z!Z!!z!Z!Z!!'); 
console.log(repeat('', 3) === ''); 
console.log(repeat('abc', 0) === '');
// 均印出 true

這是最簡單的測試方法,但這麼做的缺點是很難「規模化」。我們可以利用別人寫好的框架來便利測試。

利用現成的框架 Jest 測試

  1. 用 npm 下載 Jest:輸入指令 npm install -save-dev jest

  1. 利用模組將「測試」與「要測試的 Function」分開。
  • 在要測試的檔案 index.js 加上:module.exports = '要輸出的值'
function repeat(str, times) {
  let result = ''
  for (let i = 0; i < times; i += 1) {
    result += str
  }
  return result
}

module.exports = repeat
  • 建立 index.test.js 檔案:touch index.test.js,習慣用 test.js 取名
    並在檔案中引用 index.js 輸出的值:var repeat = require('./引入的檔案')
// 可輸入 node index.test.js 測試是否引入成功
var repeat = require('./index')

console.log(repeat('a', 5));
// 印出 aaaaa,引入成功
  • 在 index.test.js 加入 Jest 語法:test('描述文字', '要做的測試')
var repeat = require('./index')

test('a 重複 5 次應該要等於', function() {
  expect(repeat('a', 5)).toBe('aaaaa');
})
  1. 更新 package.json 檔案的 "scripts":加入 "test": "jest"

"test": "jest"

  1. 如此即可運行 npm run test 進行測試,看到 PASS 可知測試有成功:

test

之所以要用 npm 來跑 jest,而不是直接在終端機輸入 jest 指令,是因為 jest 只安裝在該專案底下,要使用時才會拿出來用。

若只想測特定檔案,可以修改 "scripts""test": "jest index.test.js",後面加上檔名。

或是用 npx jest index.jest.js,同樣能夠執行測試:

也可以多跑幾個測式:

var repeat = require('./index')

test('a 重複 5 次應該要等於aaaaa', function() {
  expect(repeat('a', 5)).toBe('aaaaa');
});

test('abc 重複 1 次應該要等於abc', function () {
  expect(repeat('abc', 1)).toBe('abc');
});

test(' "" 重複 10 次應該要等於 ""', function () {
  expect(repeat('', 10)).toBe('');
});

測試結果

也可以把測試項目放在 describe() 函式裡,這種寫法會更有結構一點:

// 架構:

`describe('測試 XX', function(){
  test('名稱', function() {
    expect('回傳值').toBe('預期結果');
  })
})`
var repeat = require('./index')

describe('測試 repeat', function() {
  test('a 重複 5 次應該要等於aaaaa', function () {
    expect(repeat('a', 5)).toBe('aaaaa');
  });

  test('abc 重複 1 次應該要等於abc', function () {
    expect(repeat('abc', 1)).toBe('abc');
  });

  test(' "" 重複 10 次應該要等於 ""', function () {
    expect(repeat('', 10)).toBe('');
  });
})

Unit test 單元測試

單元測試指的是測試一個工作單元(a unit of work)的行為。上述範例測試單一函式,就是一種單元測試。可用來確認每個 Unit 是否正確,也能夠進行規模化測試。


補充:如何在 Windows 上執行 Jest 也能有紅綠標籤

方法:把 package.json 裡的 "script" 中的內容改為 "test": "jest --colors",FAIL 和 PASS 標籤就會是紅綠標籤。

color

奇怪的是,在 git commit 時都沒有問題,在執行 Jest 時卻出現亂碼。不太確定是不是因為更改 locale 才解決的,總之介面很神奇的變成中文了,可喜可賀。

UTF-8

參考資料:

  1. Colorful output in Bash terminal does not work (–colors option)

先寫測試再寫程式:TDD

Test-driven Development(測試驅動開發),是一種開發流程,簡言之就是「先寫測試在開發」。相較於傳統的「先開發在寫測試」模式,TDD 有幾項優點:

  1. 能確保測試程式的撰寫
  2. 從使用方觀點切入,有助於在開發初期釐清程式介面如何設計
  3. 便於日後 Debug

實作 TDD

可利用 Jest 模組,先建立 test() 架構,再來撰寫主要程式碼。步驟可參考:TDD 開發五步驟,帶你實戰 Test-Driven Development 範例

步驟一、選定一個目標功能,來新增測試案例

先寫好測試預期結果,並盡量列出邊界條件。在這個步驟還不會撰寫目標程式內容。這裡用 reverse 函式為例:

// 先寫出預期結果:

var reverse = require('./index')

describe('測試 reverse', function() {
  test('123 reverse 要等於 321', function () {
    expect(reverse('123')).toBe('321');
  });

  test('!!! reverse 要等於 !!!', function () {
    expect(reverse('!!!')).toBe('!!!');
  });

  test(' "" reverse 要等於 ""', function () {
    expect(reverse('')).toBe('');
  });
})

步驟二、執行測試,得到 Failed(紅燈)

因為還沒撰寫目標程式,結果就會是 Failed。此步驟目的是確保測試程式可執行,沒有語法錯誤。

運行測試

步驟三、實作「最低限度」的產品程式

開始寫程式,以能夠通過測試為目標,不求將程式碼優化一步到位。完成到一個階段就可運行 jest 查看是否有錯誤:

//開始寫程式

function reverse(str) {
  let result = ''
  for (let i = str.length - 1; i >= 0; i -= 1) {
    result += str[i]
  }
  return result
}

module.exports = reverse

步驟四:再次執行測試,得到 Passed(綠燈)

在這個階段,即完成一個可運作且正確的程式版本,包含產品程式和測試程式。

Passed

步驟五:重構程式

最後是優化「產品程式」和「測試程式」的程式碼,因為測試程式也是專案需維護的一部份。如此即可提升程式的可讀性、可維護性、擴充性。