用Jest測試JavaScript

Jest是用於測試JavaScript代碼的庫。這是Facebook維護的一個開源項目,它特別適合於React代碼測試,儘管不僅限於此:它可以測試任何JavaScript代碼。笑話非常快速且易於使用

笑話簡介

Jest是用於測試JavaScript代碼的庫。

這是Facebook維護的一個開源項目,它特別適合於React代碼測試,儘管不僅限於此:它可以測試任何JavaScript代碼。它的優勢是:

  • 它很快
  • 它可以執行快照測試
  • 固執己見,並提供所有現成的內容,而無需您做出選擇

Jest是與Mocha非常相似的工具,儘管它們之間存在差異:

  • 摩卡(Mocha)固執己見,而傑斯特(Jest)有一些約定
  • Mocha需要更多的配置,而Jest通常開箱即用,這要歸功於他們自以為是
  • 摩卡(Mocha)更古老,更成熟,具有更多的工具集成

在我看來,Jest的最大特點是它是一種開箱即用的解決方案,無需與其他測試庫進行交互即可執行其工作。

安裝

Jest會自動安裝在create-react-app,因此,如果您使用它,則無需安裝Jest。

可以使用以下命令將Jest安裝在任何其他項目中

yarn add --dev jest

或者npm

npm install --save-dev jest

請注意,我們如何指示雙方將玩笑放在devDependencies的一部分package.json文件,以便僅將其安裝在開發環境中,而不能在生產環境中安裝。

將此行添加到您的腳本部分package.json文件:

{
  "scripts": {
    "test": "jest"
  }
}

這樣就可以使用yarn test或者npm run test

或者,您可以全局安裝Jest:

yarn global add jest

然後使用jest命令行工具。

創建第一個Jest測試

創建的項目create-react-app開箱即用地安裝並預先配置了Jest,但是將Jest添加到任何項目就像鍵入一樣容易

yarn add --dev jest

加到你的package.json這行:

{
  "scripts": {
    "test": "jest"
  }
}

並通過執行運行測試yarn test在你的殼裡。

現在,您在這裡沒有任何測試,因此將不執行任何操作:

Testing with Yarn

讓我們創建第一個測試。打開一個math.js文件並輸入幾個函數,我們稍後將對其進行測試:

const sum = (a, b) => a + b
const mul = (a, b) => a * b
const sub = (a, b) => a - b
const div = (a, b) => a / b

module.exports = { sum, mul, sub, div }

現在創建一個math.test.js文件,放在同一文件夾中,我們將在其中使用Jest測試在中定義的功能math.js

const { sum, mul, sub, div } = require('./math')

test(‘Adding 1 + 1 equals 2’, () => { expect(sum(1, 1)).toBe(2) }) test(‘Multiplying 1 * 1 equals 1’, () => { expect(mul(1, 1)).toBe(1) }) test(‘Subtracting 1 - 1 equals 0’, () => { expect(sub(1, 1)).toBe(0) }) test(‘Dividing 1 / 1 equals 1’, () => { expect(div(1, 1)).toBe(1) })

跑步yarn test導致Jest在找到的所有測試文件上運行,並向我們返回最終結果:

Passing tests

用VS Code運行Jest

Visual Studio Code是JavaScript開發的出色編輯器。這玩笑擴展為我們的測試提供了一流的集成。

安裝後,它將自動檢測是否已在devDependencies中安裝了Jest並運行測試。您還可以通過選擇開玩笑:亞軍命令。每當您更改具有測試的文件之一(或測試文件)時,它將運行測試並停留在監視模式下以重新運行它們:

A simple Jest test running in VS Code

匹配器

在上一篇文章中,我使用了toBe()作為唯一的匹配器

test('Adding 1 + 1 equals 2', () => {
  expect(sum(1, 1)).toBe(2)
})

匹配器是一種允許您測試值的方法。

最常用的匹配器,比較結果的值expect()使用作為參數傳遞的值是:

  • toBe比較嚴格平等,使用===
  • toEqual比較兩個變量的值。如果是對像或數組,則檢查所有屬性或元素的相等性
  • toBeNull傳遞空值時為true
  • toBeDefined傳遞定義的值時為true(與上述相反)
  • toBeUndefined傳遞未定義的值時為true
  • toBeCloseTo用於比較浮點值,避免舍入錯誤
  • toBeTruthy如果該值被視為true,則返回true(例如if做)
  • toBeFalsy如果該值被認為是假的,則返回true(例如if做)
  • toBeGreaterThan如果Expect()的結果高於參數,則為true
  • toBeGreaterThanOrEqual如果Expect()的結果等於參數,或者高於參數,則為true
  • toBeLessThan如果Expect()的結果小於參數,則為true
  • toBeLessThanOrEqual如果Expect()的結果等於參數,或者小於參數,則為true
  • toMatch用於比較字符串正則表達式模式匹配
  • toContain用於數組中,如果期望的數組在其元素集中包含參數,則為true
  • toHaveLength(number):檢查數組的長度
  • toHaveProperty(key, value):檢查對像是否具有屬性,並可選地檢查其值
  • toThrow檢查您傳遞的函數是否拋出異常(通常)或特定異常
  • toBeInstanceOf():檢查對像是否是類的實例

所有這些匹配器都可以使用.not.在語句中,例如:

test('Adding 1 + 1 does not equal 3', () => {
  expect(sum(1, 1)).not.toBe(3)
})

與諾言一起使用,您可以使用.resolves.rejects

expect(Promise.resolve('lemon')).resolves.toBe('lemon')

expect(Promise.reject(new Error(‘octopus’))).rejects.toThrow(‘octopus’)

設置

在運行測試之前,您將需要執行一些初始化。

要在所有測試運行之前執行一次操作,請使用beforeAll()功能:

beforeAll(() => {
  //do something
})

要在每次測試運行之前執行某些操作,請使用beforeEach()

beforeEach(() => {
  //do something
})

拆除

就像您可以進行設置一樣,您還可以在每次測試運行後執行一些操作:

afterEach(() => {
  //do something
})

在所有測試結束後:

afterAll(() => {
  //do something
})

使用describe()進行組測試

您可以在單個文件中創建測試組,以隔離設置和拆卸功能:

describe('first set', () => {
  beforeEach(() => {
    //do something
  })
  afterAll(() => {
    //do something
  })
  test(/*...*/)
  test(/*...*/)
})

describe(‘second set’, () => { beforeEach(() => { //do something }) beforeAll(() => { //do something }) test(//) test(//) })

測試異步代碼

現代JavaScript中的異步代碼基本上可以有兩種形式:回調和Promise。除了承諾,我們還可以使用async / await。

回呼

您不能在回調中進行測試,因為Jest不會執行測試-測試文件的執行在調用回調之前結束。要解決此問題,請將參數傳遞給測試函數,您可以方便地調用它done。開玩笑會等到你打電話done()在結束測試之前:

//uppercase.js
function uppercase(str, callback) {
  callback(str.toUpperCase())
}
module.exports = uppercase

//uppercase.test.js const uppercase = require(’./src/uppercase’)

test(uppercase 'test' to equal 'TEST', (done) => { uppercase(‘test’, (str) => { expect(str).toBe(‘TEST’) done() } })

Jest async test callback

承諾

通過返回承諾的功能,我們兌現承諾從測試:

//uppercase.js
const uppercase = str => {
  return new Promise((resolve, reject) => {
    if (!str) {
      reject('Empty string')
      return
    }
    resolve(str.toUpperCase())
  })
}
module.exports = uppercase

//uppercase.test.js const uppercase = require(’./uppercase’) test(uppercase 'test' to equal 'TEST', () => { return uppercase(‘test’).then(str => { expect(str).toBe(‘TEST’) }) })

Jest async test promises

可以用以下方法測試被拒絕的承諾.catch()

//uppercase.js
const uppercase = str => {
  return new Promise((resolve, reject) => {
    if (!str) {
      reject('Empty string')
      return
    }
    resolve(str.toUpperCase())
  })
}

module.exports = uppercase

//uppercase.test.js const uppercase = require(’./uppercase’)

test(uppercase 'test' to equal 'TEST', () => { return uppercase(’’).catch(e => { expect(e).toMatch(‘Empty string’) }) })

Jest async test catch

異步/等待

為了測試返回promise的函數,我們還可以使用async / await,這使得語法非常簡單明了:

//uppercase.test.js
const uppercase = require('./uppercase')
test(`uppercase 'test' to equal 'TEST'`, async () => {
  const str = await uppercase('test')
  expect(str).toBe('TEST')
})

Jest async test await async

模擬

在測試中嘲笑允許您測試依賴於以下功能的功能:

  • 數據庫
  • 網絡要求
  • 進入檔案文件
  • 任何外部的系統

以便:

  1. 您的測試運行快點,在開發過程中提供了快速的周轉時間
  2. 你的測試是獨立的網絡條件或數據庫狀態
  3. 您的測試不污染任何數據存儲,因為它們不接觸數據庫
  4. 測試中所做的任何更改都不會更改後續測試的狀態,並且重新運行測試套件應從已知且可複制的起點開始
  5. 您不必擔心API調用和網絡請求的速率限制

當您想要避免副作用(例如,寫入數據庫)或想要跳過代碼的慢速部分(例如,網絡訪問),並且還可以避免多次運行測試所帶來的影響(例如,假設某個函數發送一個錯誤的代碼)時,模擬非常有用。電子郵件或調用受速率限制的API)。

更重要的是,如果您正在編寫單元測試,您應該單獨測試功能的功能,而不是對其所有涉及的事物進行測試。

使用模擬,您可以通過以下方式檢查是否已調用模塊函數以及使用了哪些參數:

  • expect().toHaveBeenCalled():檢查是否已調用間諜函數
  • expect().toHaveBeenCalledTimes():計算一個間諜函數被調用了多少次
  • expect().toHaveBeenCalledWith():檢查是否已使用一組特定的參數調用了該函數
  • expect().toHaveBeenLastCalledWith():檢查上次調用該函數的參數

間諜軟件包而不會影響功能代碼

導入包時,您可以使用以下命令告訴Jest對特定功能的執行“間諜”:spyOn(),而不會影響該方法的工作方式。

例子:

const mathjs = require('mathjs')

test(The mathjs log function, () => { const spy = jest.spyOn(mathjs, ‘log’) const result = mathjs.log(10000, 10)

expect(mathjs.log).toHaveBeenCalled() expect(mathjs.log).toHaveBeenCalledWith(10000, 10) })

模擬整個包裹

Jest提供了一種模擬整個程序包的便捷方法。創建一個__mocks__文件夾在項目根目錄中,並在此文件夾中為每個包創建一個JavaScript文件。

說你匯入mathjs。創建一個__mocks__/mathjs.js文件放在您的項目根目錄中,並添加以下內容:

module.exports = {
  log: jest.fn(() => 'test')
}

這將模擬包的log()函數。添加任意數量的要模擬的函數:

const mathjs = require('mathjs')

test(The mathjs log function, () => { const result = mathjs.log(10000, 10) expect(result).toBe(‘test’) expect(mathjs.log).toHaveBeenCalled() expect(mathjs.log).toHaveBeenCalledWith(10000, 10) })

模擬一個功能

您可以使用以下方法模擬單個函數jest.fn()

const mathjs = require('mathjs')

mathjs.log = jest.fn(() => ‘test’) test(The mathjs log function, () => { const result = mathjs.log(10000, 10) expect(result).toBe(‘test’) expect(mathjs.log).toHaveBeenCalled() expect(mathjs.log).toHaveBeenCalledWith(10000, 10) })

您也可以使用jest.fn().mockReturnValue('test')創建一個簡單的模擬,除了返回值外不執行任何操作。

預先建立的模擬

您可以找到流行庫的預製模型。例如這個包https://github.com/jefflau/jest-fetch-mock讓你嘲笑fetch()調用,並提供示例返回值,而無需與測試中的實際服務器進行交互。

快照測試

快照測試是Jest提供的一項很酷的功能。它可以記住UI組件的呈現方式,並將其與當前測試進行比較,如果不匹配,則會引發錯誤。

這是對簡單的App組件的簡單測試create-react-app應用程序(確保已安裝react-test-renderer):

import React from 'react'
import App from './App'
import renderer from 'react-test-renderer'

it(‘renders correctly’, () => { const tree = renderer.create(<App />).toJSON() expect(tree).toMatchSnapshot() })

第一次運行此測試時,Jest將快照保存到__snapshots__文件夾。這是App.test.js.snap包含的內容:

// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`renders correctly 1`] = `
<div
  className="App"
>
  <header
    className="App-header"
  >
    <img
      alt="logo"
      className="App-logo"
      src="logo.svg"
    />
    <h1
      className="App-title"
    >
      Welcome to React
    </h1>
  </header>
  <p
    className="App-intro"
  >
    To get started, edit
    <code>
      src/App.js
    </code>
     and save to reload.
  </p>
</div>
`

如您所見,這是App組件呈現的代碼,僅此而已。

下次測試比較輸出<App />對此。如果應用程序發生更改,則會出現錯誤:

Error with snapshot

使用時yarn testcreate-react-app你在觀看模式,然後您可以按w並顯示更多選項:

Watch Usage
 › Press u to update failing snapshots.
 › Press p to filter by a filename regex pattern.
 › Press t to filter by a test name regex pattern.
 › Press q to quit watch mode.
 › Press Enter to trigger a test run.

If your change is intended, pressing u will update the failing snapshots, and make the test pass.

You can also update the snapshot by running jest -u (or jest --updateSnapshot) outside of watch mode.


More devtools tutorials: