jest 自動化測試

wang_yb發表於2018-04-19

概述

jest 是 facebook 開源的,用來進行單元測試的框架,可以測試 javascipt 和 react。 單元測試各種好處已經被說爛了,這裡就不多扯了。重點要說的是,使用 jest, 可以降低寫單元測試的難度。

單元測試做得好,能夠極大提高軟體的質量,加快軟體迭代更新的速度, 但是,單元測試也不是銀彈,單元測試做得好,並不是測試框架好就行,其實單元測試做的好不好,很大程度上取決於程式碼寫的是否易於測試。 單元測試不僅僅是測試程式碼,通過編寫單元測試 case,也可以反過來重構已有的程式碼,使之更加容易測試。

jest 的零配置思路是我最喜歡的特性。

安裝方式

# yarn
yarn add --dev jest

# OR npm
npm install --save-dev jest

測試方式

隨著 node.js 的發展,javascipt 語言的發展,前端可以勝任越來越多的工作,後端很多時候反而淪為資料庫的 http API 所以前端不僅僅是測試看得見的頁面,還有很多看不見的邏輯需要測試。

jest 提供了非常方便的 API,可以對下面的場景方便的測試

一般函式

對於一般的函式,參考附錄中的 jest Expect API 可以很容易的寫測試 case 待測試檔案:joinPath.js

const separator = '/'
const replace = new RegExp(separator + '{1,}', 'g')
export default (...parts) => parts.join(separator).replace(replace, separator)

測試檔案的命名:joinPath.test.js 採用這種命名,jest 會自動找到這個檔案來執行

import joinPath from 'utils/joinPath'

test('join path 01', () => {
  const path1 = '/a/b/c'
  const path2 = '/d/e/f'

  expect(joinPath(path1, path2)).toBe('/a/b/c/d/e/f')
})

test('join path 02', () => {
  const path1 = '/a/b/c/'
  const path2 = '/d/e/f'

  expect(joinPath(path1, path2)).toBe('/a/b/c/d/e/f')
})

test('join path 03', () => {
  const path1 = '/a/b/c/'
  const path2 = 'd/e/f'

  expect(joinPath(path1, path2)).toBe('/a/b/c/d/e/f')
})

非同步函式

上面的是普通函式,對於非同步函式,比如 ajax 請求,測試寫法同樣容易 待測試檔案:utils/client.js

export const get = (url, headers = {}) => {
  return fetch(url, {
    method: 'GET',
    headers: {
      ...getHeaders(),
      ...headers
    }
  }).then(parseResponse)
}

測試檔案:client.test.js

import { get } from 'utils/client'

test('fetch by get method', async () => {
  expect.assertions(1)
  // 測試使用了一個免費的線上 JSON API
  const url = 'https://jsonip.com/'

  const data = await get(url)
  const { about } = data
  expect(about).toBe('/about')
})

測試的生命週期

jest 測試提供了一些測試的生命週期 API,可以輔助我們在每個 case 的開始和結束做一些處理。 這樣,在進行一些和資料相關的測試時,可以在測試前準備一些資料,在測試後,清理測試資料。

4 個主要的生命週期函式:

  • afterAll(fn, timeout): 當前檔案中的所有測試執行完成後執行 fn, 如果 fn 是 promise,jest 會等待 timeout 毫秒,預設 5000
  • afterEach(fn, timeout): 每個 test 執行完後執行 fn,timeout 含義同上
  • beforeAll(fn, timeout): 同 afterAll,不同之處在於在所有測試開始前執行
  • beforeEach(fn, timeout): 同 afterEach,不同之處在於在每個測試開始前執行
BeforeAll(() => {
  console.log('before all tests to excute !')
})

BeforeEach(() => {
  console.log('before each test !')
})

AfterAll(() => {
  console.log('after all tests to excute !')
})

AfterEach(() => {
  console.log('after each test !')
})

Test('test lifecycle 01', () => {
  expect(1 + 2).toBe(3)
})

Test('test lifecycle 03', () => {
  expect(2 + 2).toBe(4)
})

mock

我自己覺得能不 mock,還是儘量不要 mock,很多時候覺得程式碼不好測試而使用 mock,還不如看看如何重構程式碼,使之不用 mock 也能測試。 對於某些函式中包含的一些用時過長,或者呼叫第三方庫的地方,而這些地方並不是函式的主要功能, 那麼,可以用 mock 來模擬,從而提高測試執行的速度。

對於上面非同步函式的例子,我們改造成 mock 的方式:

const funcUseGet = async url => {
  return await get(url)
}

test('mock fetch get method', async () => {
  const client = require('utils/client')
  client.get = jest.fn(url => ({ mock: 'test' }))

  const url = 'http://mock.test'
  const data = await funcUseGet(url)

  expect(data).toEqual({ mock: 'test' })
})

react 測試

jest 可以測試 react component,但是我們用了狀態分離的方式開發前端, 大部分功能都在 action 中,這部分測試基本沒有做。

附錄

相關文章