PJCHENder 未整理筆記

[React] 使用 Jest 來測試 React 元件(testing)

2019-12-28

[React] 使用 Jest 來測試 React 元件(testing)

keywords: test, react, jest, CLI
1
2
$ npm test                  # 執行測試
$ npm test -- --coverage # 檢視測試覆蓋率

安裝

當你透過 create-react-app 來建立 React App 時,已經內建好 Jest 作為測試的執行器(test runner)。Jest 是 Node-based 的執行器,主要是用來進行元件的單元測試(unit test)而非 DOM 本身,雖然透過 jsdom 可以取得瀏覽器中的全域變數,但仍只是趨近瀏覽器的行為,因此若你需要的進行更貼近瀏覽器環境的 end-to-end 測試的話,建議可以使用 cypresspuppeteer

檔名慣例(Filename Conventions)

Jest 會自動去找根目錄(src)下所有符合下列命名慣例的檔案:

  • __tests__ 資料夾中,並以 .js 結尾的檔案
  • 檔案名稱以 .test.js 結尾的檔案
  • 檔案名稱以 .spec.js 結尾的檔案

建議把執行測試的檔案和原本的檔案放在一起,如此可以複雜的 import 路徑。舉例來說,把 App.jsApp.test.js 放在同一個資料夾中,如此在執行測試時只需要寫 import App from './App' 即可。

Command Line Interface(CLI)

當執行 npm test 時,Jest 會自動進入「監控模式(watch mode)」,每當你儲存檔案它都會自動重新執行測試。這個監控器(watcher)包含一個 CLI 可以讓你選擇要執行所有的測試,還是只執行符合條件下的檔案來測試。

1
2
3
$ npm test
$ jest --watch
$ jest --watchAll=false # 不要監控

與版本控制整合

預設的情況下,當你執行 npm test 之後,Jest 只會針對上次 commit 後有變更的檔案進行測試,同時這也假設了你不會經常把沒有通過測試的檔案 commit 起來

在 CLI 中你可以點擊 a 來強制 Jest 執行所有的測試。

Jest 在 continuous integration server 上、並非 Git 或 Mercurial 資料夾的專案總是會執行全部的測試。

單元測試

要建立測試,只需使用 it()test() 函式,並在參數中放入該測試的名稱和程式碼,你可以選擇性(optional)的把它們包在 describe() 區塊內。

在 Jest 中提供內建的 expect() 函式來建立斷言(assertions),基本的測試會長得像這樣:

1
2
3
4
5
6
import sum from './sum';

it('sums numbers', () => {
expect(sum(1, 2)).toEqual(3);
expect(sum(2, 2)).toEqual(4);
});

元件測試

這裡建立最基本測試元件的方式。這個測試會掛載(mounts)元件來測試它在轉譯過程中不會拋出錯誤:

1
2
3
4
5
6
7
8
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App />, div);
});

react-testing-library

react-testing-library 可以用來模擬 React 元件被使用者使用的情況,它可以適用在 React 元件和應用程式的 unit, integration, end-to-end 測試,並且建議可以搭配 jest-dom 來針對 DOM 建立斷言。

💡 透過 create-react-app 建立的 React 專案已經預裝好 react-testing-libraryjest-dom。

1
2
3
// src/setupTests.js
// 這支檔案會在執行測試前自動被執行
import '@testing-library/jest-dom/extend-expect';
1
2
3
4
5
6
7
8
// src/App.test.js
import React from 'react';
import { render } from '@testing-library/react';
import App from './App';
it('renders welcome message', () => {
const { getByText } = render(<App />);
expect(getByText('Learn React')).toBeInTheDocument();
});

測試環境設定與初始化

在測試中可能會使用到瀏覽器的 API 或進行全域環境的設定,這時候可以在專案中加入 src/setupTests.js 這支檔案,這支檔案會在執行測試前自動被執行

舉例來說:

1
2
3
4
5
6
7
8
9
// src/setupTests.js
// 這支檔案會在執行測試前自動被執行
const localStorageMock = {
getItem: jest.fn(),
setItem: jest.fn(),
removeItem: jest.fn(),
clear: jest.fn(),
};
global.localStorage = localStorageMock;

⚠️ 若曾經 eject 過

若你在建立 src/setupTests.js 前已經把 create-react-app 所建立的專案執行 eject,那麼 package.json 檔案將不會包含任何參照到 setupTests.js 的資訊,因此應該要在手動添加設定:

1
2
3
4
5
// package.json
"jest": {
// ...
"setupTestFrameworkScriptFile": "<rootDir>/src/setupTests.js"
}

專注和略過某些測試

只需要把 it() 改成 xit() 就可以暫時略過某些測試項目;fit() 則可以讓你專注於某個測試項目而不用去執行其他的測試。

測試覆蓋率報告(Coverage Reporting)

執行 npm test -- --coverage 就可以取得測試覆蓋率的報告。這個部分有許多設定可以自行修改,可以參考 configuration 的部分。

快照測試(Snapshot Testing)

Jest 提供了一個快照測試(snapshot testing),它會自動將元件產生一個文字版的快照,並儲存在硬碟上,每當 UI 改變時,就會收到通知,此時你可以檢視這個變更是不是預期的改變,是的話可以執行 jest -u,否則的話就去修復這個 bug。

參考:Jest 14.0: React Tree Snapshot Testing @ Jest

其他問題

無法使用 __mocks__ 資料夾

透過 create-react-app 建立的 React 專案目前(2020-01-22)可能會吃不到 __mocks__ 資料夾中的設定,解決方法是把 __mocks__ 的資料夾搬到 src 裡面。

參考資料:

掃描二維條碼,分享此文章