JavaScript >> Javascript 文檔 >  >> JavaScript

在帶有 Sinon.js 的 JavaScript 中使用存根進行測試

簡介

測試是軟件開發過程的基本部分。在創建 Web 應用程序時,我們會調用我們環境中的第三方 API、數據庫或其他服務。因此,我們的測試必須驗證這些請求是否已發送並且響應是否正確處理。但是,在運行測試時,我們可能並不總是能夠與那些外部服務進行通信。

在我們的本地開發計算機上,我們可能沒有公司 API 密鑰或數據庫憑據來成功運行測試。這就是為什麼我們有時會使用 stub “偽造” HTTP 或數據庫響應 ,欺騙我們的代碼,使其表現得像發出了真正的請求。

在本文中,我們將首先了解什麼是存根以及我們為什麼要使用它們。然後,我們將利用流行的 JavaScript 測試庫 Sinon.js 為 JavaScript 創建單元測試,以存根 HTTP 請求。

然後,我們將在 Spies 和 Mocks 上發表文章跟進:

  • 在帶有 Sinon.js 的 JavaScript 中使用存根進行測試(你在這裡 )
  • 通過 Sinon.js 在 JavaScript 中使用間諜進行測試
  • 在帶有 Sinon.js 的 JavaScript 中使用 Mocks 進行測試

什麼是存根?

測試存根是用固定響應替換模塊的實際行為的函數或對象。存根只能返回它被編程返回的固定響應。

存根可以看作是我們測試的一個假設——如果我們假設一個外部服務返回這個響應,這就是函數的行為方式。

想像一下,您有一個接受 HTTP 請求並從 GraphQL 端點獲取數據的函數。如果我們無法在我們的測試中連接到 GraphQL 端點,我們將存根它的響應,以便我們的代碼將像 GraphQL 實際被命中一樣運行。我們的函數代碼不會知道實際的 GraphQL 響應與我們的存根響應之間的區別。

讓我們看看存根有用的場景。

為什麼要使用存根?

在測試中向外部服務發出請求時,可能會遇到以下問題:

  • 由於網絡連接錯誤而不是代碼錯誤導致測試失敗
  • 運行時間較長,因為網絡延遲會增加測試時間
  • 如果發生配置錯誤,則會通過測試錯誤地影響生產數據

我們可以通過隔離我們的測試並存根這些外部服務調用來解決這些問題。不會有網絡依賴,使我們的測試更可預測並且不太可能失敗。如果沒有網絡延遲,我們的測試預計也會更快。

在某些情況下,外部請求不起作用。例如,在 CI/CD 構建過程中,出於安全原因,在運行測試時阻止外部請求是很常見的。也有可能有時我們會編寫依賴於仍在開發中且未處於待使用狀態的服務的代碼。

在這些情況下,存根非常有用,因為它允許我們在服務不可用時測試我們的代碼。

現在我們知道了 stub 是什麼以及它們為什麼有用,讓我們使用 Sinon.js 來獲得 stub 的實踐經驗。

使用 Sinon.js 創建 Stub

我們將使用 Sinon.js 存根來自 JSON API 的響應,該響應檢索相冊中的照片列表。我們的測試將使用 Mocha 和 Chai 測試庫創建。如果您想在繼續之前了解有關使用 Mocha 和 Chai 進行測試的更多信息,可以按照我們的指南進行操作。

設置

首先,在您的終端中創建一個新文件夾並移至其中:

$ mkdir PhotoAlbum
$ cd PhotoAlbum

初始化 NPM,以便您可以跟踪您安裝的包:

$ npm init -y

一旦完成,我們就可以開始安裝我們的依賴項了。首先,讓我們安裝請求庫,我們的代碼將使用它來創建對 API 的 HTTP 請求。在終端中輸入:

$ npm i request --save

現在,讓我們將所有測試庫安裝為開發依賴項。測試代碼不用於生產,因此我們不會將測試庫安裝為帶有 --save 的常規代碼依賴項 選項。相反,我們將使用 --save-dev 選項告訴 NPM 這些依賴項只能在我們的開發/測試環境中使用。在終端輸入命令:

$ npm i mocha chai sinon --save-dev

導入所有庫後,我們將創建一個新的 index.js 文件並添加代碼以在此處發出 API 請求。您可以使用終端創建 index.js 文件:

$ touch index.js

在您的文本編輯器或 IDE 中,編寫以下代碼:

免費電子書:Git Essentials

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

const request = require('request');

const getPhotosByAlbumId = (id) => {
    const requestUrl = `https://jsonplaceholder.typicode.com/albums/${id}/photos?_limit=3`;
    return new Promise((resolve, reject) => {
        request.get(requestUrl, (err, res, body) => {
            if (err) {
                return reject(err);
            }
            resolve(JSON.parse(body));
        });
    });
};

module.exports = getPhotosByAlbumId;

此函數調用一個 API,該 API 返回相冊中的照片列表,其 ID 作為參數傳遞給函數。我們將響應限制為僅返回三張照片。

現在我們將為我們的函數編寫測試以確認它按預期工作。我們的第一個測試不會使用存根,而是會發出實際的請求。

沒有存根的測試

首先,讓我們創建一個文件來編寫我們的測試。在終端或其他地方,製作一個 index.test.js 當前目錄下的文件:

$ touch index.test.js

我們的代碼將測試我們返回了三張照片,並且每張照片都有預期的 id , title , 和 url 屬性。

index.test.js 文件,添加以下代碼:

const expect = require('chai').expect;
const getPhotosByAlbumId = require('./index');

describe('withoutStub: getPhotosByAlbumId', () => {
    it('should getPhotosByAlbumId', (done) => {
        getPhotosByAlbumId(1).then((photos) => {
            expect(photos.length).to.equal(3);
            photos.forEach(photo => {
                expect(photo).to.have.property('id');
                expect(photo).to.have.property('title');
                expect(photo).to.have.property('url');
            });
            done();
        });
    });
});

在這個測試中,我們首先需要 expect() 來自 Chai 的函數,然後需要 getPhotosByAlbumId() index.js 中的函數 文件。

我們使用 Mocha 的 describe()it() 函數,所以我們可以使用 mocha 命令來運行代碼作為測試。

在運行我們的測試之前,我們需要在 package.json 中添加一個腳本來運行我們的測試。在 package.json 文件,添加以下內容:

"scripts": {
    "test": "mocha index.test.js"
}

現在使用以下命令運行您的測試:

$ npm test

你應該看到這個輸出:

$ mocha index.test.js

  withoutStub: getPhotosByAlbumId
    ✓ should getPhotosByAlbumId (311ms)

  1 passing (326ms)

在這種情況下,測試需要 326 毫秒才能運行,但這可能會因您的互聯網速度和位置而異。

如果您沒有活動的 Internet 連接,則此測試將不會通過,因為 HTTP 請求將失敗。儘管這並不意味著該函數的行為不符合預期。讓我們使用一個存根,這樣我們就可以在沒有網絡依賴的情況下測試我們的函數的行為。

用存根測試

讓我們重寫我們的函數,以便我們將請求存根到 API,返回預定義的照片列表:

const expect = require('chai').expect;
const request = require('request');
const sinon = require('sinon');
const getPhotosByAlbumId = require('./index');

describe('with Stub: getPhotosByAlbumId', () => {
    before(() => {
        sinon.stub(request, 'get')
            .yields(null, null, JSON.stringify([
                {
                    "albumId": 1,
                    "id": 1,
                    "title": "accusamus beatae ad facilis cum similique qui sunt",
                    "url": "https://via.placeholder.com/600/92c952",
                    "thumbnailUrl": "https://via.placeholder.com/150/92c952"
                },
                {
                    "albumId": 1,
                    "id": 2,
                    "title": "reprehenderit est deserunt velit ipsam",
                    "url": "https://via.placeholder.com/600/771796",
                    "thumbnailUrl": "https://via.placeholder.com/150/771796"
                },
                {
                    "albumId": 1,
                    "id": 3,
                    "title": "officia porro iure quia iusto qui ipsa ut modi",
                    "url": "https://via.placeholder.com/600/24f355",
                    "thumbnailUrl": "https://via.placeholder.com/150/24f355"
                }
            ]));
    });

    after(() => {
        request.get.restore();
    });

    it('should getPhotosByAlbumId', (done) => {
        getPhotosByAlbumId(1).then((photos) => {
            expect(photos.length).to.equal(3);
            photos.forEach(photo => {
                expect(photo).to.have.property('id');
                expect(photo).to.have.property('title');
                expect(photo).to.have.property('url');
            });
            done();
        });
    });
});

在運行測試之前,我們告訴 Sinon.js 存根 get() request的功能 getPhotosByAlbumId () 中使用的對象 .

傳遞給 yields() 的參數 存根的函數是將傳遞給 get 請求的回調的參數。我們通過 null 對於 errres 參數,以及 body 的假相冊數據數組 參數。

和之前一樣,我們使用 npm test 運行這個測試 .您應該會看到以下輸出:

$ mocha index.test.js

  with Stub: getPhotosByAlbumId
    ✓ should getPhotosByAlbumId

  1 passing (37ms)

偉大的!現在沒有互聯網連接,我們仍然確信我們的函數可以很好地處理預期的數據。測試也跑得更快!沒有網絡請求,我們只需要從內存中獲取數據。

結論

存根是在調用時返回固定數據的函數的替代品。我們通常將請求存根到外部系統,以使測試運行更具可預測性並消除對網絡連接的需求。

Sinon.js 可以與其他測試框架一起使用來存根函數。在本文中,我們將 HTTP GET 請求存根,這樣我們的測試就可以在沒有互聯網連接的情況下運行。也減少了測試時間。

如果您想查看本教程的代碼,可以在這裡找到。

在我們的下一篇文章中,我們將繼續介紹 Sinon.js,並介紹如何使用間諜來測試 JavaScript。


Tutorial JavaScript 教程
  1. jQueryPlugin:返回 this 與返回 this.each()

  2. 小鬍子,使用外部模板

  3. 到目前為止,我使用 Chakra-Ui 學到了什麼

  4. 用新的 js 功能替換 lodash.get

  5. ⚛️ 🚀 React 組件模式

  6. JavaScript 引擎⚙️💛

  7. 為什麼有些語句不能與布爾運算符一起使用

  1. 使用免費工具優化無服務器應用程序

  2. 在輸入中的每 3 個數字後添加空格

  3. 像 create-react-app 一樣生成你的 web-app 樣板。

  4. 將 HTMLCollection 轉換為數組的最有效方法

  5. 如何構建 Chrome 擴展程序

  6. 這個項目在大學裡給了我 A++ 💯🎓 這是我最後的帖子😭

  7. 將數組增加到最大項目

  1. 學習使用認知服務將 AI 添加到您的應用程序

  2. 通過 CDN (Cloudfront) 交付 Meteor CSS/JS 包和公共資產

  3. 把 38 變成 2:如何解決加位數問題

  4. 我製作了一個應用程序,顯示您的鍵盤和鼠標使用情況(帶有鍵盤熱圖)