Kiểm tra JavaScript với Jest

Jest là một thư viện để kiểm tra mã JavaScript. Đó là một dự án mã nguồn mở được duy trì bởi Facebook và nó đặc biệt rất phù hợp để kiểm tra mã React, mặc dù không giới hạn ở điều đó: nó có thể kiểm tra bất kỳ mã JavaScript nào. Jest rất nhanh và dễ sử dụng

Giới thiệu về Jest

Jest là một thư viện để kiểm tra mã JavaScript.

Đó là một dự án mã nguồn mở được duy trì bởi Facebook và nó đặc biệt rất phù hợp để kiểm tra mã React, mặc dù không giới hạn ở điều đó: nó có thể kiểm tra bất kỳ mã JavaScript nào. Điểm mạnh của nó là:

  • nó nhanh
  • nó có thể thực hiệnkiểm tra ảnh chụp nhanh
  • nó kiên định và cung cấp mọi thứ mà không yêu cầu bạn đưa ra lựa chọn

Jest là một công cụ rất giống với Mocha, mặc dù chúng có những điểm khác biệt:

  • Mocha ít cố chấp hơn, trong khi Jest có một số quy ước nhất định
  • Mocha yêu cầu nhiều cấu hình hơn, trong khi Jest thường hoạt động hiệu quả, nhờ vào sự kiên định
  • Mocha lâu đời hơn và lâu đời hơn, với nhiều tích hợp công cụ hơn

Theo ý kiến của tôi, tính năng lớn nhất của Jest là nó là một giải pháp out of the box hoạt động mà không cần phải tương tác với các thư viện thử nghiệm khác để thực hiện công việc của nó.

Cài đặt

Jest được tự động cài đặt trongcreate-react-app, vì vậy nếu bạn sử dụng nó, bạn không cần phải cài đặt Jest.

Jest có thể được cài đặt trong bất kỳ dự án nào khác bằng cách sử dụngSợi:

yarn add --dev jest

hoặc lànpm:

npm install --save-dev jest

lưu ý cách chúng tôi hướng dẫn cả hai cách đưa Jest vàodevDependenciesmột phần củapackage.jsonđể nó chỉ được cài đặt trong môi trường phát triển chứ không phải trong sản xuất.

Thêm dòng này vào phần tập lệnh củapackage.jsontập tin:

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

để các bài kiểm tra có thể được chạy bằng cách sử dụngyarn testhoặc lànpm run test.

Ngoài ra, bạn có thể cài đặt Jest trên toàn cầu:

yarn global add jest

và chạy tất cả các thử nghiệm của bạn bằng cách sử dụngjestcông cụ dòng lệnh.

Tạo thử nghiệm Jest đầu tiên

Các dự án được tạo bằngcreate-react-appđã cài đặt và định cấu hình sẵn Jest ra khỏi hộp, nhưng việc thêm Jest vào bất kỳ dự án nào cũng dễ dàng như nhập

yarn add --dev jest

Thêm vào của bạnpackage.jsonđường thẳng này:

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

và chạy các thử nghiệm của bạn bằng cách thực hiệnyarn testtrong vỏ của bạn.

Bây giờ, bạn không có bất kỳ bài kiểm tra nào ở đây, vì vậy sẽ không có gì được thực thi:

Testing with Yarn

Hãy tạo thử nghiệm đầu tiên. Mở mộtmath.jstệp và nhập một vài hàm mà sau này chúng tôi sẽ kiểm tra:

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 }

Bây giờ hãy tạo mộtmath.test.jstrong cùng một thư mục và ở đó, chúng tôi sẽ sử dụng Jest để kiểm tra các chức năng được xác định trongmath.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) })

Đang chạyyarn testkết quả là Jest được chạy trên tất cả các tệp thử nghiệm mà nó tìm thấy và trả lại cho chúng tôi kết quả cuối cùng:

Passing tests

Chạy Jest với VS Code

Visual Studio Code là một trình soạn thảo tuyệt vời để phát triển JavaScript. CácPhần mở rộng Jestcung cấp tích hợp hàng đầu cho các thử nghiệm của chúng tôi.

Sau khi bạn cài đặt nó, nó sẽ tự động phát hiện nếu bạn đã cài đặt Jest trong devDependencies của mình và chạy các bài kiểm tra. Bạn cũng có thể gọi các bài kiểm tra theo cách thủ công bằng cách chọnJest: Start Runnerchỉ huy. Nó sẽ chạy các bài kiểm tra và ở chế độ xem để chạy lại chúng bất cứ khi nào bạn thay đổi một trong các tệp có bài kiểm tra (hoặc tệp thử nghiệm):

A simple Jest test running in VS Code

Người đối sánh

Trong bài viết trước tôi đã sử dụngtoBe()là duy nhấtngười kết hợp:

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

Trình so khớp là một phương pháp cho phép bạn kiểm tra các giá trị.

Các trình so khớp được sử dụng phổ biến nhất, so sánh giá trị của kết quảexpect()với giá trị được truyền vào dưới dạng đối số, là:

  • toBeso sánh bình đẳng nghiêm ngặt, sử dụng===
  • toEqualso sánh các giá trị của hai biến. Nếu đó là một đối tượng hoặc mảng, nó sẽ kiểm tra sự bình đẳng của tất cả các thuộc tính hoặc phần tử
  • toBeNulllà đúng khi truyền giá trị null
  • toBeDefinedlà true khi chuyển một giá trị xác định (ngược lại với giá trị trên)
  • toBeUndefinedlà đúng khi chuyển một giá trị không xác định
  • toBeCloseTođược sử dụng để so sánh các giá trị nổi, tránh lỗi làm tròn
  • toBeTruthytrue nếu giá trị được coi là true (như mộtiflàm)
  • toBeFalsyđúng nếu giá trị được coi là sai (như mộtiflàm)
  • toBeGreaterThantrue nếu kết quả của kỳ vọng () cao hơn đối số
  • toBeGreaterThanOrEqualtrue nếu kết quả của kỳ vọng () bằng đối số hoặc cao hơn đối số
  • toBeLessThantrue nếu kết quả của kỳ vọng () thấp hơn đối số
  • toBeLessThanOrEqualtrue nếu kết quả của kỳ vọng () bằng đối số hoặc thấp hơn đối số
  • toMatchđược sử dụng để so sánh các chuỗi vớibiểu hiện thông thườngphù hợp với mô hình
  • toContainđược sử dụng trong mảng, true nếu mảng mong đợi chứa đối số trong tập hợp phần tử của nó
  • toHaveLength(number): kiểm tra độ dài của một mảng
  • toHaveProperty(key, value): kiểm tra xem một đối tượng có thuộc tính hay không và tùy chọn kiểm tra giá trị của nó
  • toThrowkiểm tra xem một hàm bạn truyền có ném ra một ngoại lệ (nói chung) hoặc một ngoại lệ cụ thể hay không
  • toBeInstanceOf(): kiểm tra xem một đối tượng có phải là một thể hiện của một lớp hay không

Tất cả các đối sánh đó có thể bị phủ nhận bằng cách sử dụng.not.bên trong câu lệnh, ví dụ:

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

Để sử dụng với những lời hứa, bạn có thể sử dụng.resolves.rejects:

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

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

Thiết lập

Trước khi chạy các bài kiểm tra, bạn sẽ muốn thực hiện một số khởi tạo.

Để làm điều gì đó một lần trước khi tất cả các thử nghiệm chạy, hãy sử dụngbeforeAll()chức năng:

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

Để thực hiện điều gì đó trước mỗi lần chạy thử nghiệm, hãy sử dụngbeforeEach():

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

Phá bỏ

Cũng giống như bạn có thể làm với thiết lập, bạn cũng có thể thực hiện một số việc sau mỗi lần chạy thử nghiệm:

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

và sau khi tất cả các thử nghiệm kết thúc:

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

Kiểm tra nhóm bằng cách sử dụng description ()

Bạn có thể tạo các nhóm kiểm tra, trong một tệp duy nhất, tách biệt các chức năng thiết lập và chia nhỏ:

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

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

Kiểm tra mã không đồng bộ

Mã không đồng bộ trong JavaScript hiện đại về cơ bản có thể có 2 dạng: gọi lại và hứa hẹn. Ngoài những lời hứa, chúng ta có thể sử dụng async / await.

Gọi lại

Bạn không thể có một bài kiểm tra trong một cuộc gọi lại, vì Jest sẽ không thực thi nó - việc thực thi tệp kiểm tra kết thúc trước khi lệnh gọi lại được gọi. Để khắc phục điều này, hãy chuyển một tham số cho hàm kiểm tra, bạn có thể gọi hàm này một cách thuận tiệndone. Jest sẽ đợi cho đến khi bạn gọidone()trước khi kết thúc bài kiểm tra đó:

//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

Lời hứa

Với các hàm trả về lời hứa, chúng tôitrả lại một lời hứatừ bài kiểm tra:

//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

Có thể kiểm tra những lời hứa bị từ chối bằng cách sử dụng.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

Async / await

Để kiểm tra các hàm trả về lời hứa, chúng ta cũng có thể sử dụng async / await, điều này làm cho cú pháp rất đơn giản và dễ hiểu:

//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

Chế giễu

Trong thử nghiệm,chế giễucho phép bạn kiểm tra chức năng phụ thuộc vào:

  • Cơ sở dữ liệu
  • Mạng lướiyêu cầu
  • truy cập vàoCác tập tin
  • bất kìBên ngoàihệ thống

vậy nên:

  1. các bài kiểm tra của bạn chạynhanh hơn, mang lại thời gian quay vòng nhanh chóng trong quá trình phát triển
  2. bài kiểm tra của bạn làđộc lậpđiều kiện mạng hoặc trạng thái của cơ sở dữ liệu
  3. các bài kiểm tra của bạn khôngô nhiễmbất kỳ lưu trữ dữ liệu nào vì chúng không chạm vào cơ sở dữ liệu
  4. bất kỳ thay đổi nào được thực hiện trong thử nghiệm không làm thay đổi trạng thái cho các thử nghiệm tiếp theo và việc chạy lại bộ thử nghiệm phải bắt đầu từ điểm xuất phát đã biết và có thể tái lập
  5. bạn không phải lo lắng về việc giới hạn tốc độ đối với các cuộc gọi API và yêu cầu mạng

Chế nhạo rất hữu ích khi bạn muốn tránh các tác dụng phụ (ví dụ: ghi vào cơ sở dữ liệu) hoặc bạn muốn bỏ qua các phần mã chậm (như truy cập mạng) và cũng tránh các tác động khi chạy thử nghiệm của bạn nhiều lần (ví dụ: hãy tưởng tượng một hàm gửi email hoặc gọi một API giới hạn tỷ lệ).

Quan trọng hơn nữa, nếu bạn đang viếtKiểm tra đơn vị, bạn nên kiểm tra chức năng của một chức năng một cách riêng biệt, không phải với tất cả những thứ mà nó chạm vào.

Bằng cách sử dụng mocks, bạn có thể kiểm tra xem một chức năng mô-đun đã được gọi hay chưa và những tham số nào đã được sử dụng, với:

  • expect().toHaveBeenCalled(): kiểm tra xem một hàm do thám đã được gọi chưa
  • expect().toHaveBeenCalledTimes(): đếm số lần một hàm do thám đã được gọi
  • expect().toHaveBeenCalledWith(): kiểm tra xem hàm đã được gọi với một bộ tham số cụ thể chưa
  • expect().toHaveBeenLastCalledWith(): kiểm tra các tham số của lần cuối cùng hàm đã được gọi

Các gói gián điệp mà không ảnh hưởng đến mã chức năng

Khi bạn nhập một gói, bạn có thể yêu cầu Jest “theo dõi” việc thực thi một chức năng cụ thể, bằng cách sử dụngspyOn()mà không ảnh hưởng đến cách thức hoạt động của phương pháp đó.

Thí dụ:

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) })

Mô phỏng toàn bộ gói hàng

Jest cung cấp một cách thuận tiện để giả mạo toàn bộ một gói. Tạo một__mocks__trong thư mục gốc của dự án và trong thư mục này, hãy tạo một tệp JavaScript cho mỗi gói của bạn.

Giả sử bạn nhập khẩumathjs. Tạo một__mocks__/mathjs.jstệp trong thư mục gốc dự án của bạn và thêm nội dung này:

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

Điều này sẽ giả lập hàm log () của gói. Thêm nhiều chức năng mà bạn muốn mô phỏng:

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) })

Mô phỏng một chức năng duy nhất

Bạn có thể mô phỏng một chức năng duy nhất bằng cách sử dụngjest.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) })

Bạn cũng có thể dùngjest.fn().mockReturnValue('test')để tạo một mô hình đơn giản không làm gì khác ngoài việc trả về một giá trị.

Chế tạo trước

Bạn có thể tìm thấy các bản mô phỏng được tạo sẵn cho các thư viện phổ biến. Ví dụ như gói nàyhttps://github.com/jefflau/jest-fetch-mockcho phép bạn chế nhạofetch()và cung cấp các giá trị trả về mẫu mà không cần tương tác với máy chủ thực tế trong các thử nghiệm của bạn.

Kiểm tra ảnh chụp nhanh

Kiểm tra ảnh chụp nhanh là một tính năng khá thú vị do Jest cung cấp. Nó có thể ghi nhớ cách các thành phần giao diện người dùng của bạn được hiển thị và so sánh nó với thử nghiệm hiện tại, gây ra lỗi nếu có sự không khớp.

Đây là một bài kiểm tra đơn giản trên thành phần Ứng dụng của mộtcreate-react-appứng dụng (đảm bảo bạn cài đặtreact-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() })

lần đầu tiên bạn chạy thử nghiệm này, Jest lưu ảnh chụp nhanh vào__snapshots__thư mục. Đây là những gì App.test.js.snap chứa:

// 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>
`

Như bạn thấy, đó là mã mà Thành phần ứng dụng hiển thị, không có gì khác.

Lần sau, thử nghiệm sẽ so sánh kết quả đầu ra của<App />đến điều này. Nếu Ứng dụng thay đổi, bạn sẽ gặp lỗi:

Error with snapshot

Khi đang sử dụngyarn testtrongcreate-react-appbạn đang ở trongchế độ xemvà từ đó bạn có thể nhấnwvà hiển thị thêm các tùy chọn:

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: