Jest - 讓我先試試

基本的Jest語法練習紀錄

Jest使用筆記

簡介

  • 是一個由Facebook開發的JavaScript單元測試框架
  • 單元測試(Unit Test):針對程式中的最小單元進行測試
  • 主要對於function進行測試,是否符合預期運行結果

環境建置

使用npm安裝

npm install --save-dev jest
  • package.json檔案中增加指令
  • 可以在終端機輸入 npm run test 來執行jest測試
{
  "scripts": {
    "test": "jest"
  }
}

// 查看測試覆蓋率
{
  "scripts": {
    "test": "jest --coverage"
  }
}

設定配置文件

  • 輸入指令生成 jest.config 檔案
  • 在jest 29.7版中,已經可以直接使用ES6語法,不需另外安裝Babel
npm init jest@latest
  • 會問一些配置問題,根據需求選擇 Jest 初始化時的設定問題畫面

ps: 測試環境差別

  1. Node (testEnvironment: ‘node’)
  • 適合 Node.js 應用程序和伺服器端代碼提供的環境
  • 沒有 DOM 相關的全局變數(例如 window 或 document)
  • 最適合測試純 Node.js 代碼,例如服務器端邏輯
  1. JSDOM (testEnvironment: ‘jsdom’)
  • 是一個用 JavaScript 實現的 DOM 模擬器
  • 提供 DOM 相關的全局變數,像是 window 或 document
  • 可以在 Node.js 環境中模擬瀏覽器的行為,並測試那些與 DOM 互動的代碼
  • 最適合測試前端代碼,例如 React 或 Vue 組件

基本用法

1. 建立一個js檔案

  • 撰寫需要使用的function
  • 使用module.exports 把function輸出
// sum.js
function sum(a, b) {
  return a + b
}
module.exports = sum

2. 建立一個test.js檔案

  • 使用require引入要測試的function
//sum.test.js
const sum = require('./sum')

test('測試 8+9 會等於17', () => {
  expect(sum(8, 9)).toBe(17) // passed
})

3. 基本語法

  • test / it:定義一個單元測試
test('該測試的說明', () => {
  expect(要測試的function).toBe(預期的結果)
})

it('該測試的說明', () => {
  expect(要測試的function).toBe(預期的結果)
})
  • describe:定義群組單元測試,裡面可以放多個test
describe('測試加法函式', () => {
  test('測試 8+9 會等於17', () => {
    expect(sum(8, 9)).toBe(17)
  })

  test('測試 1+1 會等於2', () => {
    expect(sum(1, 1)).toBe(2)
  })
})

🍞 Modifiers

  • not:用於表示否定
expect(1 + 2).not.toBe(4)
  • resolves:檢查一個 promise 是否成功
const promise = Promise.resolve('success')
expect(promise).resolves.toBe('success')
  • rejects:檢查一個 promise 是否失敗
const promise = Promise.reject(new Error('failure'))
expect(promise).rejects.toThrow('failure')

🍞 常用的Matchers

  • toBe:使用Object.is來比較是否相等,常用於原始型別
  • toEqual:進行深層次的值比較,適用於Object和Array的比較
test('object assign', () => {
  const a = {}
  expect(a).toBe({}) // failed
  expect(a).toEqual({}) // passed
})
  • toBeTruthy:檢查值是否為 truthy
  • toBeFalsy:檢查值是否為 falsy
describe('truthy or falsey', () => {
  test('truthy', () => {
    expect('Hello').toBeTruthy()
  })

  test('falsey', () => {
    expect(0).toBeFalsy()
  })
})
  • toContain:檢查陣列是否包含某個特定元素
expect(['apple', 'banana', 'cherry']).toContain('banana')
  • toThrow: 檢查函數是否拋出錯誤
const throwFunction = () => {
  throw new Error('error!')
}
expect(throwFunction).toThrow('error!')

Number的比較

  • toBeGreaterThan
  • toBeGreaterThanOrEqual
  • toBeLessThan
  • toBeLessThanOrEqual
describe('numbers', () => {
  const value = 8 + 9
  test('8加9', () => {
    expect(value).toBe(17)
    expect(value).toBeGreaterThan(16)
    expect(value).toBeLessThan(21)
    expect(value).toBeLessThanOrEqual(18)
  })
})

🍞 非同步函式測試

  • 在node環境安裝axios
  • 取得一個todo測試
// async.js
const axios = require('axios')

const fetchData = async (id) => {
  const res = await axios.get(`https://jsonplaceholder.typicode.com/todos/${id}`)
  return res.data
}

module.exports = fetchData
  • 因為是非同步行為,要加上async…await
const fetchData = require('./async')

test('it should return a todo', async () => {
  const todo = await fetchData(1)
  expect(todo.id).toBe(1)
})

🍞 常在群組測試使用的方法

  1. beforeEach: 在每一個單元測試之前執行
  2. afterEach: 在每一個單元測試之後執行
  3. beforeAll: 在所有單元測試之前執行一次
  4. afterAll: 在所有單元測試之後執行一次
  • 寫在describe內,表示該組測試才會用到
// 使用 beforeEach 和 afterEach
let counter = 0

describe('Counter Tests', () => {
  beforeAll(() => {
    console.log('測試開始了')
  })

  afterAll(() => {
    console.log('測試結束囉')
  })

  beforeEach(() => {
    counter = 0
  })

  afterEach(() => {
    console.log(`Counter after test: ${counter}`)
  })

  test('Increment counter', () => {
    counter++
    expect(counter).toBe(1)
  })

  test('Decrement counter', () => {
    counter--
    expect(counter).toBe(-1)
  })
})

4. Mock用法

  • 可以不受外部依賴(資料庫 , API…),單純模擬測試功能
  1. jest.fn:創建模擬函式
const mockFn = jest.fn((scalar) => 42 + scalar)

mockFn(0) // 42
mockFn(1) // 43
  1. mockReturnValue:設定回傳的值
const mockFn = jest.fn()
mockFn.mockReturnValue(123)

expect(mockFunction()).toBe(123)
  1. mockReturnValueOnce:只回傳一次,用於在連續調用中回傳不同的值
const mockFn = jest.fn()
mockFn.mockReturnValueOnce('first call').mockReturnValueOnce('second call')

// 首次調用
expect(mockFunction()).toBe('first call')

// 第二次調用
expect(mockFunction()).toBe('second call')
  1. jest.spyOn:用於偵測某物件的某方法是否被調用,也可以使用它來模擬方法的返回值或行為
  • 使用 jest.spyOn(axios, 'get') 來創建一個觀察 axiosget 方法的”間諜”
  • 這個”間諜”會攔截所有對 axios.get 的調用,並對它進行記錄
  • 使用 .mockReturnValueOnce() 方法來模擬 axios.get 在下一次調用時的返回值
// asynctest.test.js

const fetchData = require('./async')
const axios = require('axios')

// 模擬axios.get
test('mock axios', async () => {
  const spy = jest.spyOn(axios, 'get').mockReturnValueOnce({
    data: {
      id: 1,
      todo: 'Do Homework!',
    },
  })
  const res = await fetchData(1)
  expect(res.todo).toBe('Do Homework!')

  spy.mockRestore()
})

參考資料