JavaScript >> Javascript 文檔 >  >> JavaScript

在 Jest 中使用 Mocks 在 JavaScript 中進行測試

簡介

Jest 是一個流行的開源 JavaScript 測試框架。我們可以使用 Jest 在我們的測試中創建模擬 - 對像在測試時替換我們代碼中的真實對象。

在我們之前關於使用 Sinon.js 進行單元測試技術的系列中,我們介紹瞭如何使用 Sinon.js 來存根、監視和模擬 Node.js 應用程序——尤其是 HTTP 調用。

在本系列中,我們將使用 Jest 介紹 Node.js 中的單元測試技術。 Jest 由 Facebook 創建,並與許多 JavaScript 庫和框架很好地集成,例如 React、Angular 和 Vue 等等。它特別注重簡單性和性能。

在本文中,我們將回顧什麼是模擬,然後重點介紹如何為 Node.js 應用程序設置 Jest,以在我們的測試中模擬 HTTP 調用。然後我們將比較我們如何使用 Jest 和 Sinon 為我們的程序創建模擬。

什麼是 Mocks?

在單元測試中,模擬為我們提供了對依賴項提供的功能進行存根的能力,以及一種觀察我們的代碼如何與依賴項交互的方法。當將依賴項直接包含到我們的測試中很昂貴或不切實際時,Mocks 尤其有用,例如,在您的代碼對 API 進行 HTTP 調用或與數據庫層交互的情況下。

最好將這些依賴項的響應存根,同時確保根據需要調用它們。這就是 mock 派上用場的地方。

現在讓我們看看如何使用 Jest 在 Node.js 中創建模擬。

在 Node.js 應用程序中設置 Jest

在本教程中,我們將設置一個 Node.js 應用程序,該應用程序將對包含相冊中照片的 JSON API 進行 HTTP 調用。 Jest 將用於在我們的測試中模擬 API 調用。

首先,讓我們創建文件所在的目錄並移入其中:

$ mkdir PhotoAlbumJest && cd PhotoAlbumJest

然後,讓我們使用默認設置初始化 Node 項目:

$ npm init -y

項目初始化後,我們將繼續創建 index.js 目錄根目錄下的文件:

$ touch index.js

為了幫助我們處理 HTTP 請求,我們將使用 Axios。

設置 Axios

我們將使用 axios 作為我們的 HTTP 客戶端。 Axios 是一個輕量級、基於 Promise 的 Node.js HTTP 客戶端,也可以被 Web 瀏覽器使用。這使它非常適合我們的用例。

我們先安裝吧:

$ npm i axios --save

使用 axios 之前 ,我們將創建一個名為 axiosConfig.js 的文件 通過它我們將配置 Axios 客戶端。配置客戶端允許我們在一組 HTTP 請求中使用通用設置。

例如,我們可以為一組 HTTP 請求設置授權標頭,或者最常見的是,將用於所有 HTTP 請求的基本 URL。

讓我們創建配置文件:

touch axiosConfig.js

現在,讓我們訪問 axios 並配置基本 URL:

const axios = require('axios');

const axiosInstance = axios.default.create({
    baseURL: 'https://jsonplaceholder.typicode.com/albums'
});

module.exports = axiosInstance;

設置baseURL後 ,我們已經導出了 axios 實例,以便我們可以在我們的應用程序中使用它。我們將使用 www.jsonplaceholder.typicode.com 這是一個用於測試和原型設計的虛假在線 REST API。

index.js 我們之前創建的文件,讓我們定義一個函數,它返回給定相冊 ID 的照片 URL 列表:

const axios = require('./axiosConfig');

const getPhotosByAlbumId = async (id) => {
    const result = await axios.request({
        method: 'get',
        url: `/${id}/photos?_limit=3`
    });
    const { data } = result;
    return data;
};

module.exports = getPhotosByAlbumId;

要訪問我們的 API,我們只需使用 axios.request() axios 的方法 實例。我們傳入方法的名稱,在我們的例子中是 geturl 我們將調用它。

我們傳遞給 url 的字符串 字段將連接到 baseURL 來自 axiosConfig.js .

現在,讓我們為這個函數設置一個 Jest 測試。

設置 Jest

要設置 Jest,我們必須首先使用 npm 將 Jest 作為開發依賴項安裝 :

$ npm i jest -D

-D flag 是 --save-dev 的快捷方式 ,它告訴 NPM 將其保存為開發依賴項。

然後我們將繼續為 Jest 創建一個名為 jest.config.js 的配置文件 :

touch jest.config.js

現在,在 jest.config.js 文件,我們將設置測試所在的目錄:

module.exports = {
    testMatch: [
        '<rootDir>/**/__tests__/**/?(*.)(spec|test).js',
        '<rootDir>/**/?(*.)(spec|test).js'
    ],
    testEnvironment: 'node',
};

免費電子書:Git Essentials

查看我們的 Git 學習實踐指南,其中包含最佳實踐、行業認可的標準以及隨附的備忘單。停止谷歌搜索 Git 命令並真正學習 它!

testMatch value 是 Jest 用於檢測測試文件的全局模式數組。在我們的例子中,我們指定 __tests__ 內的任何文件 目錄或我們項目中具有 .spec.js 的任何位置 或 .test.js 擴展名應被視為測試文件。

注意 :在 JavaScript 中,測試文件以 .spec.js 結尾是很常見的 .開發人員使用“規範” 作為“規範”的簡寫 .這意味著測試包含正在實現的功能的功能要求或規範。

testEnvironment value 表示 Jest 運行的環境,即在 Node.js 中還是在瀏覽器中。您可以在此處閱讀有關其他允許的配置選項的更多信息。

現在讓我們修改我們的 package.json 測試腳本,以便它使用 Jest 作為我們的測試運行器:

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

我們的設置完成了。要測試我們的配置是否有效,請在目錄的根目錄下創建一個名為 index.spec.js 的測試文件 :

touch index.spec.js

現在,在文件中,讓我們編寫一個測試:

describe('sum of 2 numbers', () => {
    it(' 2 + 2 equal 4', () => {
        expect(2 + 2).toEqual(4)
    });
});

使用以下命令運行此代碼:

$ npm test

你應該得到這個結果:

 PASS  ./index.spec.js
  sum of 2 numbers
    ✓ 2 + 2 equal 4 (3ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        0.897s, estimated 1s
Ran all test suites.

正確設置 Jest 後,我們現在可以繼續編寫代碼來模擬我們的 HTTP 調用。

使用 Jest 模擬 HTTP 調用

index.spec.js 文件,我們將重新開始,刪除舊代碼並編寫一個模擬 HTTP 調用的新腳本:

const axios = require('./axiosConfig');
const getPhotosByAlbumId = require('./index');

jest.mock('./axiosConfig', () => {
    return {
        baseURL: 'https://jsonplaceholder.typicode.com/albums',
        request: jest.fn().mockResolvedValue({
            data: [
                {
                    albumId: 3,
                    id: 101,
                    title: 'incidunt alias vel enim',
                    url: 'https://via.placeholder.com/600/e743b',
                    thumbnailUrl: 'https://via.placeholder.com/150/e743b'
                },
                {
                    albumId: 3,
                    id: 102,
                    title: 'eaque iste corporis tempora vero distinctio consequuntur nisi nesciunt',
                    url: 'https://via.placeholder.com/600/a393af',
                    thumbnailUrl: 'https://via.placeholder.com/150/a393af'
                },
                {
                    albumId: 3,
                    id: 103,
                    title: 'et eius nisi in ut reprehenderit labore eum',
                    url: 'https://via.placeholder.com/600/35cedf',
                    thumbnailUrl: 'https://via.placeholder.com/150/35cedf'
                }
            ]
        }),
    }
});

在這裡,我們首先使用 require 導入我們的依賴項 句法。由於我們不想進行任何實際的網絡調用,我們創建了一個手動模擬 axiosConfig 使用 jest.mock() 方法。 jest.mock() 方法將模塊路徑作為參數,並將模塊的可選實現作為工廠參數 .

對於 factory 參數,我們指定我們的 mock,axiosConfig , 應該返回一個由 baseURL 組成的對象 和 request() . baseUrl 設置為 API 的基本 URL。 request() 是一個返回照片數組的模擬函數。

request() 我們在這裡定義的函數替換了真正的 axios.request() 功能。當我們調用 request() 方法,我們的模擬方法將被調用。

需要注意的是 jest.fn() 功能。它返回一個新的模擬函數 , 其實現在括號內定義。我們通過 mockResolvedValue() 所做的事情 函數為 request() 提供了一個新的實現 功能。

通常,這是通過 mockImplementation() 完成的 函數,但因為我們實際上只是返回 data 它保存了我們的結果——我們可以使用 Sugar 函數。

mockResolvedValue()mockImplementation(() => Promise.resolve(value)) 相同 .

有了一個 mock,讓我們繼續編寫一個測試:

describe('test getPhotosByAlbumId', () => {
    afterEach(() => jest.resetAllMocks());

    it('fetches photos by album id', async () => {
        const photos = await getPhotosByAlbumId(3);
        expect(axios.request).toHaveBeenCalled();
        expect(axios.request).toHaveBeenCalledWith({ method: 'get', url: '/3/photos?_limit=3' });
        expect(photos.length).toEqual(3);
        expect(photos[0].albumId).toEqual(3)
    });
});

在每個測試用例之後,我們確保 jest.resetAllMocks() 調用函數來重置所有模擬的狀態。

在我們的測試用例中,我們調用 getPhotosByAlbumId() ID 為 3 的函數 作為論據。然後我們做出斷言。

第一個斷言期望 axios.request() 方法被調用,而第二個斷言檢查是否使用正確的參數調用了該方法。我們還檢查返回數組的長度是 3 並且數組的第一個對像有一個 albumId 3 .

讓我們運行我們的新測試:

npm test

我們應該得到以下結果:

PASS  ./index.spec.js
  test getPhotosByAlbumId
    ✓ fetches photos by album id (7ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        0.853s, estimated 1s
Ran all test suites.

有了這個新的熟悉和體驗,我們來快速對比一下 Jest 和 Sinon 的測試體驗,這也是 mocking 常用的方式。

Sinon Mocks vs Jest Mocks

Sinon.js 和 Jest 有不同的方式來處理模擬的概念。以下是一些需要注意的主要區別:

  • 在 Jest 中,當您將模擬文件放在 __mocks__ 中時,Node.js 模塊會在您的測試中自動模擬 node_modules 旁邊的文件夾 文件夾。例如,如果您的文件名為 __mock__/fs.js ,然後每次 fs 在您的測試中調用模塊,Jest 將自動使用模擬。另一方面,使用 Sinon.js,您必須使用 sinon.mock() 手動模擬每個依賴項 每個需要它的測試的方法。
  • 在 Jest 中,我們使用 jest.fn().mockImplementation() 用存根響應替換模擬函數的實現的方法。可以在此處的 Jest 文檔中找到一個很好的示例。在 Sinon.js 中,我們使用 mock.expects() 處理方法。
  • Jest 提供了大量方法來處理他們的模擬 API,尤其是模塊。你可以在這裡查看所有這些。另一方面,Sinon.js 使用模擬的方法較少,並且公開了通常更簡單的 API。
  • Sinon.js mocks 作為 Sinon.js 庫的一部分提供,可以插入並與 Mocha 等其他測試框架和 Chai 等斷言庫結合使用。另一方面,Jest 模擬作為 Jest 框架的一部分提供,該框架還提供了自己的斷言 API。

當您測試一個可能不需要像 Jest 這樣的框架的全部功能的小型應用程序時,Sinon.js 模擬通常是最有用的。當您已經有一個測試設置並且需要向應用程序中的一些組件添加模擬時,它也很有用。

但是,在處理具有許多依賴項的大型應用程序時,利用 Jest 的模擬 API 及其框架的強大功能對於確保一致的測試體驗非常有益。

結論

在本文中,我們研究瞭如何使用 Jest 模擬使用 axios 進行的 HTTP 調用 .我們首先將應用程序設置為使用 axios 作為我們的 HTTP 請求庫,然後設置 Jest 來幫助我們進行單元測試。最後,我們回顧了 Sinon.js 和 Jest 模擬之間的一些差異,以及我們何時可以最好地使用它們。

要詳細了解 Jest 模擬以及如何將它們用於更高級的用例,請在此處查看其文檔。

與往常一樣,本教程的代碼可以在 GitHub 上找到。


Tutorial JavaScript 教程
  1. Kissjs 第一眼:4 分鐘構建一個待辦事項應用程序

  2. 你想做一個應用程序嗎?

  3. React 的人性化介紹(和 JSX,以及一些 ES 6)

  4. 使用 Docker Compose 進行 NodeJS 開發

  5. 我想修復單擊菜單按鈕的問題

  6. 如何使用 Javascript 計算元素的 XPath 位置?

  7. 4 天 3 場會議:NodeSummit、ForwardJS 和 npmCamp 2016

  1. 在紅帽 OpenShift 上使用 Node.js 14

  2. Webpack:溫和的介紹

  3. 我們回來了!黑客馬拉松 - 烏克蘭的新獎品、日期和捐款

  4. 在 chrome 擴展中調整 popup.html 的大小

  5. 對像數組 - 第 2 部分

  6. JavaScript-30-Day-6

  7. 關於第三方 Javascript – 原則

  1. JavaScript調試器實用介紹;

  2. TypeError:store.getState 不是函數。 (在‘store.getState()’中,‘store.getState’未定義我該如何解決這個問題?

  3. 帶有 TypeScript 的 Vue.js 2 和 Vuex 3

  4. 在 Vue.js 中清理 HTML