來幫你的程式寫測試吧!


Posted by saffran on 2021-02-05

要怎麼測試我寫好的程式是對的還是錯的?

測試方式一:用 console.log() 來做測試(最陽春的測試方式)

自己想一些測試資料,也要記得測試一些 edge case

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

console.log(repeat('love', 3))
console.log(repeat('c!!', 5))
console.log(repeat('hey', 0))
console.log(repeat('', 8))

output:

lovelovelove
c!!c!!c!!c!!c!!

但是,用這樣 console.log(repeat('love', 3)) 的方式來寫的話,比較麻煩的是:印出結果後,我還要去 CLI 對照結果是否正確

因此,優化的方式是:
直接在 console.log() 裡面寫上答案,如果輸出的結果是 true,就代表是正確的

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

console.log(repeat('love', 3) === 'lovelovelove')
console.log(repeat('c!!', 5) === 'c!!c!!c!!c!!c!!')
console.log(repeat('hey', 0) === '')
console.log(repeat('', 8) === '')

output:

true
true
true
true

使用 console.log() 做測試的缺點

  • 很難規模化
  • 測試完還要自己去看是不是 true,好麻煩

測試方式二:利用 Jest 來寫測試

Jest 是一個由 Facebook 開發的前端 framework(是 open source 的),可以幫助我更方便、更有架構的完成「測試程式」的目標

用 Jest 做的測試又稱為 unit test(單元測試),較常用在測試一個一個的 function,確認每一個小的 unit(每個 function)都是正確的
(針對單一一個 function 做測試,來確認這個 function 的 input, output 是正確的)

步驟一:安裝 Jest

安裝方式一:
按照官網上的指令 npm install --save-dev jest 就只會把 jest 安裝在「專案資料夾底下」

安裝方式二(較建議):
如果是輸入指令 npm install -g jest,就可以把 jest 安裝在系統裡(參數 -g 代表 install jest globally)

步驟二:把程式輸出

「程式」跟「測試」會是分開的(用模組分開)

因此,在我的程式(也就是 repeat 函式)的下方,就會用 module.exports = repeat 做輸出
index.js:

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

module.exports = repeat

步驟三:把程式引入到測試檔案內

再新增一個檔案叫做 index.test.js

  • 這是一個慣例:我的程式的檔名是 index.js,所以測試的檔案就會叫做 index.test.js

打開 index.test.js 之後,先把我的 index.js 引入進來,順便測試一下是否引入成功

var repeat = require('./index')
console.log(repeat('love', 3))
// output: lovelovelove

步驟四:用測試模板來改寫成自己的測試

把 Jest 提供的測試模板貼過來 index.test.js 這裡

var repeat = require('./index')

// Jest 提供的模板
test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3);
});

[模板解說]
Jest 會提供我一個叫做 test 的 funciton,裡面傳入兩個參數(把參數內容改寫成自己要的):

  • 第一個參數:這串 'adds 1 + 2 to equal 3' 要填入的是「描述」,也可以寫中文,例如:'love 重複 3 次應該要是 lovelovelove'
  • 第二個參數:會傳入一個 function,這個 function 就是「我要做的測試的事情」
var repeat = require('./index')

test('adds 1 + 2 to equal 3', func);

把兩個參數都改寫成自己要的:
expect()toBe() 也都是 Jest 提供的 function

  • repeat('love', 3) 放在 expect() 這個 function 裡面
  • repeat('love', 3) 的回傳值(return),放在 toBe() 這個 function 裡面

整句話的意思就是:我預期 repeat('love', 3) 的回傳值,應該要是 'lovelovelove'

var repeat = require('./index')

test('love 重複 3 次應該要是 lovelovelove', () => {
  expect(repeat('love', 3)).toBe('lovelovelove');
});

到這裡,就寫完了我的測試!

步驟五:執行測試

執行方式一

回到 CLI 去執行 index.test.js 的測試,並不能輸入指令 node index.test.js 喔,因為這個不是 Node.js,它是 Jest,要輸入的指令是 jest

可以在 package.json 裡面的 scripts 預先寫好我想執行的指令 jest

  "scripts": {
    "start": "node index.js",
    "test": "jest"
  },

然後回到 CLI 輸入指令 npm run test,就會執行 test(也就是 jest

指令 jest 會自動去找「副檔名是 .test.js 的檔案」並且去跑測試

結果出現 PASS 就代表測試成功了!

針對單一個檔案做測試

假設,我只想要測試 index.test.js 這個檔案而已,
那就去改 package.json 裡面的 scripts,在 jest 後面加上我想要測試的檔案名稱 index.test.js

  "scripts": {
    "start": "node index.js",
    "test": "jest index.test.js"
  },

接著在 CLI 執行指令 npm run test 時,就只會針對 index.test.js 這個檔案跑測試

為什麼不能把 jest index.test.js 直接在 CLI 執行?

如果在安裝 jest 時,是按照官網上的指令 npm install --save-dev jest,那就只會把 jest 安裝在「專案資料夾底下」

這時,為什麼如果把 jest index.test.js 直接在我的 CLI 執行,就會說「找不到 jest 指令」呢?

原因是:

jest 並沒有用「globally」的方式安裝在我的電腦裡,jest 目前只有安裝在這個專案資料夾底下,因此電腦沒有設定一些東西去找到 jest 這個指令

如果是把 jest index.test.js 寫在這個專案的 package.json 的 scripts 裡面,再用 npm run test 去跑時,npm 會先去找這個專案資料夾底下的東西,發現有找到 jest 指令,因此就可以用 jest 去執行

但是,如果把 jest index.test.js 直接在系統(CLI)上面跑,就會從「系統」裡面找「是否有安裝 jest」,結果是找不到的,因為我就只有把 jest 安裝在專案底下而已

執行方式二:npx jest index.test.js 直接在 CLI 跑測試

如果 npm 的版本比較新的話,會有附 npx 這個指令
就可以直接在 CLI 使用指令 npx jest index.test.js 來跑測試

如果前面有加 npx 的話,就會在「專案」裡面找「是否有安裝 jest」,找到後就會用 jest 指令去執行,就跟我「把 jest index.test.js 寫在 package.json 裡面」是一樣的意思

但如果我直接使用指令 jest index.test.js,那就會從「系統」裡面找「我是否有安裝 jest」,但因為系統沒有裝 jest,所以就會找不到

加上很多個測試

可以寫很多個測試:

var repeat = require('./index')

test('love 重複 3 次應該要是 lovelovelove', () => {
  expect(repeat('love', 3)).toBe('lovelovelove');
});

test('hey 重複 1 次應該要是 hey', () => {
  expect(repeat('hey', 1)).toBe('hey');
});

test(' "" 重複 5 次應該要是 "" ', () => {
  expect(repeat('', 5)).toBe('');
});

describe() 函式把 test cases 組合起來

但是,目前這幾個 test cases 滿分散的,可以改成這樣更有結構的寫法:
在 index.test.js 用一個 describe() 函式把 test cases 組合起來,describe() 函式一樣是傳入兩個參數:

  • 第一個參數:'測試 repeat' 是一個字串,可以填入「測試的名稱」
  • 第二個參數:是一個 function,把 test cases 都放進去
var repeat = require('./index')

describe('測試 repeat', function(){
  test('love 重複 3 次應該要是 lovelovelove', () => {
    expect(repeat('love', 3)).toBe('lovelovelove');
  });

  test('hey 重複 1 次應該要是 hey', () => {
    expect(repeat('hey', 1)).toBe('hey');
  });

  test(' "" 重複 5 次應該要是 "" ', () => {
    expect(repeat('', 5)).toBe('');
  });
})

接著,一樣在 CLI 執行 npx jest index.test.js,就可以跑出測試結果了

另一種開發流程:TDD(先寫測試再寫程式)

TDD 的全名是 Test-driven Development(測試驅動開發)

一般的開發流程是:
先寫好 function > 再寫測試

TDD 的開發流程是:
先把測試寫好 > 再來寫 function

TDD 實際操作如下:

假設,我現在需要一個 function 可以幫我把字串倒過來(把函式取名叫做 reverse

在 index.js 先把 reverse 函式的框架寫好

function reverse(str){

}

module.exports = reverse

先寫測試

index.test.js:

var reverse = require('./index')

describe('測試 reverse', function(){
  test('abc reverse should be cba', () => {
    expect(reverse('abc')).toBe('cba');
  });

  test('!!! reverse should be !!!', () => {
    expect(reverse('!!!')).toBe('!!!');
  });

  test(' "" reverse should be "" ', () => {
    expect(reverse('')).toBe('');
  });
})

再把 reverse 函式實作出來

邊開發邊測試,直到測試 pass 為止

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

module.exports = reverse

#javascript







Related Posts

網際網路是什麼?

網際網路是什麼?

簡明程式解題入門 - 陣列篇 I

簡明程式解題入門 - 陣列篇 I

MTR04_0807

MTR04_0807


Comments