Jest 是一個用來測試 JavaScript 程式碼的函式庫。
這是一個由 Facebook 維護的開源項目,它特別適合 React 程式碼測試,但不僅限於此:它可以測試任何 JavaScript 程式碼。它的優點是:
export default function sum(a, n) { return a + b; }
divide.test.js
import sum from './sum'; // Describe the test and wrap it in a function. it('adds 1 + 2 to equal 3', () => { const result = sum(1, 2); // Jest uses matchers, like pretty much any other JavaScript testing framework. // They're designed to be easy to get at a glance; // here, you're expecting `result` to be 3. expect(result).toBe(3); });
匹配器是一種可以讓您測試值的方法。
依賴項是您的應用程式所依賴的一段程式碼。它可以是我們專案中的函數/物件或第三方依賴項(例如 axios)
當您自己的應用程式沒有一段程式碼就無法運作時,它就變成真正的依賴項。
例如,如果您在應用程式中實作一項功能來傳送電子郵件或發出 api 請求或建置設定物件等
有兩種方法我們可以在js專案的程式碼中加入依賴項:
export default function sum(a, n) { return a + b; }
只是一個簡單概念的奇特術語。
如果您的函數需要外部相依性的某些功能,只需將其作為參數注入即可。
import sum from './sum'; // Describe the test and wrap it in a function. it('adds 1 + 2 to equal 3', () => { const result = sum(1, 2); // Jest uses matchers, like pretty much any other JavaScript testing framework. // They're designed to be easy to get at a glance; // here, you're expecting `result` to be 3. expect(result).toBe(3); });
單元測試由軟體開發人員編寫和運行,以確保應用程式的一部分(稱為「單元」)滿足其設計並按預期運行。
我們想要單獨測試我們的程式碼,我們不關心任何依賴項的實際實作。
我們想要驗證
這就是模擬我們的依賴關係發揮作用的地方。
在單元測試中,模擬為我們提供了存根依賴項所提供的功能的能力,以及意味著觀察我們的程式碼如何與依賴項互動。
當將依賴項直接包含到我們的測試中成本昂貴或不切實際時,例如,當您的程式碼對 API 進行 HTTP 呼叫或與資料庫層互動時,模擬特別有用。
最好刪除這些依賴項的回應,同時確保它們按要求被呼叫。這就是模擬派上用場的地方。
透過使用模擬函數,我們可以知道以下:
建立模擬函數有多種方法。
jest.fn 方法本身就是一個高階函數。
這是一個工廠方法,用於建立新的、未使用的模擬函數。
JavaScript 中的函數是一等公民,它們可以作為參數傳遞。
每個模擬函數都有一些特殊的性質。模擬屬性是基礎。此屬性是一個對象,其中包含有關如何呼叫函數的所有模擬狀態資訊。該物件包含三個數組屬性:
export default function sum(a, n) { return a + b; }
import sum from './sum'; // Describe the test and wrap it in a function. it('adds 1 + 2 to equal 3', () => { const result = sum(1, 2); // Jest uses matchers, like pretty much any other JavaScript testing framework. // They're designed to be easy to get at a glance; // here, you're expecting `result` to be 3. expect(result).toBe(3); });
import { name, draw, reportArea, reportPerimeter } from './modules/square.js';
// Constructor Injection // DatabaseManager class takes a database connector as a dependency class DatabaseManager { constructor(databaseConnector) { // Dependency injection of the database connector this.databaseConnector = databaseConnector; } updateRow(rowId, data) { // Use the injected database connector to perform the update this.databaseConnector.update(rowId, data); } } // parameter injection, takes a database connector instance in as an argument; easy to test! function updateRow(rowId, data, databaseConnector) { databaseConnector.update(rowId, data); }
這種類型的嘲笑不太常見,原因如下:
更常見的方法是使用 jest.mock 自動將模組的所有匯出設定為 Mock 函數。
// 1. The mock function factory function fn(impl = () => {}) { // 2. The mock function const mockFn = function(...args) { // 4. Store the arguments used mockFn.mock.calls.push(args); mockFn.mock.instances.push(this); try { const value = impl.apply(this, args); // call impl, passing the right this mockFn.mock.results.push({ type: 'return', value }); return value; // return the value } catch (value) { mockFn.mock.results.push({ type: 'throw', value }); throw value; // re-throw the error } } // 3. Mock state mockFn.mock = { calls: [], instances: [], results: [] }; return mockFn; }
有時您只想觀看一個方法被調用,但保留原始實作。其他時候,您可能想要模擬實現,但稍後在套件中恢復原始版本。
test("returns undefined by default", () => { const mock = jest.fn(); let result = mock("foo"); expect(result).toBeUndefined(); expect(mock).toHaveBeenCalled(); expect(mock).toHaveBeenCalledTimes(1); expect(mock).toHaveBeenCalledWith("foo"); });
恢復原來的實現
const doAdd = (a, b, callback) => { callback(a + b); }; test("calls callback with arguments added", () => { const mockCallback = jest.fn(); doAdd(1, 2, mockCallback); expect(mockCallback).toHaveBeenCalledWith(3); });
JavaScript 是單執行緒的: 一次只能執行一個任務。通常這沒什麼大不了的,但現在想像一下你正在運行一個需要30 秒的任務.. 是的.. 在該任務期間,我們等待30 秒,然後才會發生其他事情(JavaScript 默認在瀏覽器的主線程上運行,所以整個UI都卡住了)。
現在是 2020 年,沒有人想要一個緩慢、反應遲鈍的網站。
幸運的是,瀏覽器為我們提供了一些 JavaScript 引擎本身不提供的功能:Web API。這包括 DOM API、setTimeout、HTTP 請求 等。這可以幫助我們創建一些非同步,非阻塞行為
export default function sum(a, n) { return a + b; }
import sum from './sum'; // Describe the test and wrap it in a function. it('adds 1 + 2 to equal 3', () => { const result = sum(1, 2); // Jest uses matchers, like pretty much any other JavaScript testing framework. // They're designed to be easy to get at a glance; // here, you're expecting `result` to be 3. expect(result).toBe(3); });
Jest 通常希望同步執行測試的函數。
如果我們執行非同步操作,但我們不讓 Jest 知道它應該等待測試結束,則會給出誤報。
import { name, draw, reportArea, reportPerimeter } from './modules/square.js';
非同步模式
JavaScript 中有多種處理非同步操作的模式;最常用的是:
你不能在回調中進行測試,因為 Jest 不會執行它 - 測試檔案的執行在回呼被呼叫之前結束。要解決此問題,請將參數傳遞給測試函數,您可以方便地呼叫“done”。 Jest 會等到您呼叫 done() 後再結束測試:
// Constructor Injection // DatabaseManager class takes a database connector as a dependency class DatabaseManager { constructor(databaseConnector) { // Dependency injection of the database connector this.databaseConnector = databaseConnector; } updateRow(rowId, data) { // Use the injected database connector to perform the update this.databaseConnector.update(rowId, data); } } // parameter injection, takes a database connector instance in as an argument; easy to test! function updateRow(rowId, data, databaseConnector) { databaseConnector.update(rowId, data); }
對於傳回 Promise 的函數,我們從測試中傳回一個 Promise:
// 1. The mock function factory function fn(impl = () => {}) { // 2. The mock function const mockFn = function(...args) { // 4. Store the arguments used mockFn.mock.calls.push(args); mockFn.mock.instances.push(this); try { const value = impl.apply(this, args); // call impl, passing the right this mockFn.mock.results.push({ type: 'return', value }); return value; // return the value } catch (value) { mockFn.mock.results.push({ type: 'throw', value }); throw value; // re-throw the error } } // 3. Mock state mockFn.mock = { calls: [], instances: [], results: [] }; return mockFn; }
為了測試傳回 Promise 的函數,我們也可以使用 async/await,這使得語法非常簡單明了:
test("returns undefined by default", () => { const mock = jest.fn(); let result = mock("foo"); expect(result).toBeUndefined(); expect(mock).toHaveBeenCalled(); expect(mock).toHaveBeenCalledTimes(1); expect(mock).toHaveBeenCalledWith("foo"); });
const doAdd = (a, b, callback) => { callback(a + b); }; test("calls callback with arguments added", () => { const mockCallback = jest.fn(); doAdd(1, 2, mockCallback); expect(mockCallback).toHaveBeenCalledWith(3); });
以上是Jest 簡介:單元測試、模擬和非同步程式碼的詳細內容。更多資訊請關注PHP中文網其他相關文章!