沐鳴登錄平台_Jest 初學者教程:JavaScript 測試入門

測試的意義是什麼?

在編程術語中,測試意味着檢查我們的代碼是否符合某些期望。例如:一個名為 “ transformer” 的函數應在給定某些輸入的情況下返回期望的輸出。

測試類型很多,但簡單來說測試分為三大類:

  • 單元測試
  • 集成測試
  • UI 測試

在本 Jest 教程中,我們將僅介紹單元測試,但是在本文結尾,您將找到其他類型的測試的資源。

Jest 教程:什麼是 Jest

Jest 是 JavaScript 測試運行程序,即用於創建,運行和構建測試的 JavaScript 庫。Jest 是作為 npm 軟件包分發的,您可以將其安裝在任何 JavaScript 項目中。 Jest 是目前最受歡迎的測試運行程序之一(我覺得沒有之一),也是 Create react App 的默認選擇。

首先,我怎麼知道要測試什麼?

在測試方面,即使是簡單的代碼塊也可能使新手懵逼。最常見的問題是 “我怎麼知道要測試什麼?”。如果你正在編寫 Web 應用程序,那麼一個好的切入點就是測試應用程序的每個頁面以及每個用戶的交互。但是,Web 應用程序也由功能和模塊之類的代碼單元組成,也需要進行測試。大多數情況下有兩種情況:

  • 你繼承了未經測試的舊代碼
  • 你從 0 開始新實現的功能

該怎麼做呢?對於這兩種情況,你都可以通過將測試視為代碼的一部分來進行檢查,這些代碼可以檢查給定的函數是否產生預期的結果。典型的測試流程如下所示:

  • 導入到測試的功能
  • 給定一個輸入
  • 定義期望的輸出
  • 檢查函數是否產生預期的輸出

真的,就是這樣。如果你從以下角度考慮,測試將不再可怕:輸入-預期輸出-聲明結果。稍後,我們還將看到一個方便的工具,用於幾乎準確地檢查要測試的內容。現在先用 Jest 手動測試!

Jest 教程: 初始化項目

與每個 JavaScript 項目一樣,您將需要一個 npm 環境(確保在系統上安裝了 Node)。創建一個新文件夾並使用以下命令初始化項目:

mkdir getting-started-with-jest && cd $_
npm init -y

下一步安裝 Jest:

npm i jest --save-dev

我們還需要配置一個 script,以便從命令行運行測試。打開 package.json 並配置名為 “ test” 的腳本以運行 Jest:

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

現在,你可以嗨皮的開始(入坑)了。

Jest 教程:規範和測試驅動的開發

作為開發人員,我們都喜歡創造自由。但是,當涉及到嚴重的問題時,大多數時候沒有那麼多特權。通常,我們必須遵循規範,即對構建內容的書面或口頭描述。

在本教程中,我們的項目經理提供了一個相當簡單的規範。非常重要的一點是,業務方需要一個 JavaScript 函數,該函數用來過濾一個對象數組。

對於每個對象,我們必須檢查一個名為 “ url” 的屬性,如果該屬性的值與給定的關鍵字匹配,則應在結果數組中包括匹配的對象。作為一個精通測試的 JavaScript 開發人員,你希望遵循 TDD(測試驅動開發),這是一種在開始編寫代碼之前必須編寫失敗測試的準則。

默認情況下,Jest 希望在項目文件夾中的 “ tests” 文件夾中找到測試文件。

創建新文件夾:

cd getting-started-with-jest
mkdir __tests__

接下來,在 __tests__ 目錄中中創建一個名為 filterByTerm.spec.js 的新文件。你可能想知道為什麼擴展名包含 “ .spec”。這是從 Ruby 借來的約定,用於將文件標記為給定功能的規範。

現在,讓我們進行測試!

Jest 教程:測試結構和第一個失敗的測試

是時候創建你的第一個 Jest 測試了。打開 filterByTerm.spec.js 並創建一個測試塊:

describe("Filter function", () => {
  // test stuff
});

我們的第一個朋友 describe, 一種用於包含一個或多個相關測試的 Jest 方法。

每次在開始為功能編寫新的測試套件時,都將其包裝在 describe 塊中。

如你所見,它帶有兩個參數:用於描述測試套件的字符串和用於包裝實際測試的回調函數。

接下來,我們將遇到另一個稱為 test 的函數,它是實際的測試塊:

describe("Filter function", () => {
  test("it should filter by a search term (link)", () => {
    // actual test
  });
});

至此,我們準備編寫測試了。請記住,測試是輸入,功能和預期輸出的問題。首先讓我們定義一個簡單的輸入,即對象數組:

  test("it should filter by a search term (link)", () => {
    const input = [
      { id: 1, url: "https://www.url1.dev" },
      { id: 2, url: "https://www.url2.dev" },
      { id: 3, url: "https://www.link3.dev" }
    ];
  });
});

接下來,我們將定義預期的結果。根據規範,被測函數應忽略其 url 屬性與給定搜索詞不匹配的對象。例如,我們可以期望一個帶有單個對象的數組,給定 “link” 作為搜索詞:

describe("Filter function", () => {
  test("it should filter by a search term (link)", () => {
    const input = [
      { id: 1, url: "https://www.url1.dev" },
      { id: 2, url: "https://www.url2.dev" },
      { id: 3, url: "https://www.link3.dev" }
    ];
 
    const output = [{ id: 3, url: "https://www.link3.dev" }];
  });
});

現在我們準備編寫實際的測試。我們將使用 expect 和 Jest 匹配器來檢查虛擬函數(目前)在調用時是否返回了預期結果。這是測試:

expect(filterByTerm(input, "link")).toEqual(output);

為了進一步分解內容,這是在代碼中調用該函數的方式:

filterByTerm(inputArr, "link");

在 Jest 測試中,你應該將函數調用包裝在 expect 中,並與匹配器(用於檢查輸出的 Jest 函數)一起進行實際測試。這是完整的測試:

describe("Filter function", () => {
  test("it should filter by a search term (link)", () => {
    const input = [
      { id: 1, url: "https://www.url1.dev" },
      { id: 2, url: "https://www.url2.dev" },
      { id: 3, url: "https://www.link3.dev" }
    ];
 
    const output = [{ id: 3, url: "https://www.link3.dev" }];
 
    expect(filterByTerm(input, "link")).toEqual(output);
 
  });
});

(要了解有關 Jest 匹配器的更多信息,請查閱 文檔 )。

到這裏,你可以嘗試:

npm test

你會看到,測試失敗了:

FAIL  __tests__/filterByTerm.spec.js
  Filter function
    it should filter by a search term (2ms)
 
  Filter function › it should filter by a search term (link)
 
    ReferenceError: filterByTerm is not defined
 
       9 |     const output = [{ id: 3, url: "https://www.link3.dev" }];
      10 | 
    > 11 |     expect(filterByTerm(input, "link")).toEqual(output);
         |     ^
      12 |   });
      13 | });
      14 |

“ReferenceError: filterByTerm is not defined”. 實際上,這是好事,讓我們在下一節中修復它。

Jest 教程: 修複測試(並再次讓它失敗)

真正缺少的是 filterByTerm 的實現。為了方便起見,我們將在測試所在的同一文件中創建函數。在真實的項目中,你應該在另一個文件中定義該函數,然後從測試文件中將其導入。

為了使測試通過,我們將使用一個名為 filter 的本地 JavaScript 函數,該函數能夠從數組中濾除元素。這是 filterByTerm 的最小實現:

function filterByTerm(inputArr, searchTerm) {
  return inputArr.filter(function(arrayElement) {
    return arrayElement.url.match(searchTerm);
  });
}

它是這樣工作的:對於輸入數組的每個元素,我們檢查 “ url” 屬性,並使用 match 方法將其與正則表達式進行匹配。

這是完整的代碼:

function filterByTerm(inputArr, searchTerm) {
  return inputArr.filter(function(arrayElement) {
    return arrayElement.url.match(searchTerm);
  });
}
 
describe("Filter function", () => {
  test("it should filter by a search term (link)", () => {
    const input = [
      { id: 1, url: "https://www.url1.dev" },
      { id: 2, url: "https://www.url2.dev" },
      { id: 3, url: "https://www.link3.dev" }
    ];
 
    const output = [{ id: 3, url: "https://www.link3.dev" }];
 
    expect(filterByTerm(input, "link")).toEqual(output);
  });
});

現在,再次運行測試:

npm test

然後可以看到測試通過了!

PASS  __tests__/filterByTerm.spec.js
  Filter function
     it should filter by a search term (link) (4ms)
 
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        0.836s, estimated 1s

流弊!但是我們完成測試了嗎?還沒。使我們的功能失敗需要什麼?讓我們用大寫搜索詞來強調該函數:

function filterByTerm(inputArr, searchTerm) {
  return inputArr.filter(function(arrayElement) {
    return arrayElement.url.match(searchTerm);
  });
}
 
describe("Filter function", () => {
  test("it should filter by a search term (link)", () => {
    const input = [
      { id: 1, url: "https://www.url1.dev" },
      { id: 2, url: "https://www.url2.dev" },
      { id: 3, url: "https://www.link3.dev" }
    ];
 
    const output = [{ id: 3, url: "https://www.link3.dev" }];
 
    expect(filterByTerm(input, "link")).toEqual(output);
 
    expect(filterByTerm(input, "LINK")).toEqual(output); // New test
 
  });
});

運行測試,你會發現測試失敗了,讓我們再次修復它。

Jest 教程: 修復大小寫問題

filterByTerm 還應考慮大寫搜索詞。換句話說,即使搜索詞是大寫字符串,它也應返回匹配的對象:

filterByTerm(inputArr, "link");
filterByTerm(inputArr, "LINK");

為了測試這種情況,我們引入了一個新的測試:

expect(filterByTerm(input, "LINK")).toEqual(output); // New test

為了使其通過,我們可以調整提供的正則表達式以匹配:

//
    return arrayElement.url.match(searchTerm);
//

除了可以直接傳遞 searchTerm 之外,我們可以構造一個不區分大小寫的正則表達式,即無論字符串大小寫如何都匹配的表達式。解決方法是:

function filterByTerm(inputArr, searchTerm) {
  const regex = new RegExp(searchTerm, "i");
  return inputArr.filter(function(arrayElement) {
    return arrayElement.url.match(regex);
  });
}

這是完整的測試:

describe("Filter function", () => {
  test("it should filter by a search term (link)", () => {
    const input = [
      { id: 1, url: "https://www.url1.dev" },
      { id: 2, url: "https://www.url2.dev" },
      { id: 3, url: "https://www.link3.dev" }
    ];
 
    const output = [{ id: 3, url: "https://www.link3.dev" }];
 
    expect(filterByTerm(input, "link")).toEqual(output);
 
    expect(filterByTerm(input, "LINK")).toEqual(output);
  });
});
 
function filterByTerm(inputArr, searchTerm) {
  const regex = new RegExp(searchTerm, "i");
  return inputArr.filter(function(arrayElement) {
    return arrayElement.url.match(regex);
  });
}

再次運行並看到它通過。做得好!作為練習,你可以編寫兩個新測試並檢查以下條件:

  • 測試搜索字詞 “ uRl”
  • 測試一個空的搜索詞。函數應如何處理?

你將如何組織這些新測試?

在下一節中,我們將看到測試中的另一個重要主題:代碼覆蓋率。

Jest 教程: 代碼覆蓋率

什麼是代碼覆蓋率?在談論它之前,讓我們快速調整我們的代碼。在項目根目錄 src 中創建一個新文件夾,並創建一個名為 filterByTerm.js 的文件,我們將在其中放置和導出函數:

mkdir src && cd _$
touch filterByTerm.js

filterByTerm.js 內容:

  if (!searchTerm) throw Error("searchTerm cannot be empty");
  const regex = new RegExp(searchTerm, "i");
  return inputArr.filter(function(arrayElement) {
    return arrayElement.url.match(regex);
  });
}
 
module.exports = filterByTerm;

現在,讓我們假裝我是新來的。我對測試一無所知,我沒有要求更多的上下文,而是直接進入該函數以添加新的 if 語句:

function filterByTerm(inputArr, searchTerm) {
  if (!searchTerm) throw Error("searchTerm cannot be empty");
  if (!inputArr.length) throw Error("inputArr cannot be empty"); // new line
  const regex = new RegExp(searchTerm, "i");
  return inputArr.filter(function(arrayElement) {
    return arrayElement.url.match(regex);
  });
}
 
module.exports = filterByTerm;

filterByTerm 中有一行新代碼,似乎將不進行測試。除非我告訴你 “有一個要測試的新語句”,否則你不會確切知道要在我們的函數中進行什麼樣的測試。幾乎無法想象出,我們的代碼的所有可執行路徑,因此需要一種工具來幫助發現這些盲點。

該工具稱為代碼覆蓋率,是我們工具箱中的強大工具。 Jest 具有內置的代碼覆蓋範圍,你可以通過兩種方式激活它:

  • 通過命令行傳遞標誌 “ –coverage”
  • 在 package.json 中配置 Jest

在進行覆蓋測試之前,請確保將 filterByTerm 導入 tests / filterByTerm.spec.js:

const filterByTerm = require("../src/filterByTerm");
// ...

保存文件並進行覆蓋測試:

npm test -- --coverage

你會看到下面的輸出:

 PASS  __tests__/filterByTerm.spec.js
  Filter function
     it should filter by a search term (link) (3ms)
     it should filter by a search term (uRl) (1ms)
     it should throw when searchTerm is empty string (2ms)
 
-----------------|----------|----------|----------|----------|-------------------|
File             |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
-----------------|----------|----------|----------|----------|-------------------|
All files        |     87.5 |       75 |      100 |      100 |                   |
 filterByTerm.js |     87.5 |       75 |      100 |      100 |                 3 |
-----------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total

對於我們的函數,一個非常好的總結。可以看到,第三行沒有被測試覆蓋到。下面來通過添加新的測試代碼,讓覆蓋率達到 100%。

如果要保持代碼覆蓋率始終處於開啟狀態,請在 package.json 中配置 Jest,如下所示:

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

還可以將標誌傳遞給測試腳本:

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

如果您是一個有視覺素養的人,那麼也可以使用一種 html 報告來覆蓋代碼,這就像配置 Jest 一樣簡單:

 "scripts": {
    "test": "jest"
  },
  "jest": {
    "collectCoverage": true,
    "coverageReporters": ["html"]
  },

現在,每次運行 npm test 時,您都可以在項目文件夾中訪問一個名為 coverage 的新文件夾:jest / jest / coverage /。在該文件夾中,您會發現一堆文件,其中/coverage/index.html 是代碼覆蓋率的完整 HTML 摘要。

如果單擊函數名稱,你還將看到確切的未經測試的代碼行:

非常整潔不是嗎?通過代碼覆蓋,你可以發現有疑問時要測試的內容。

Jest 教程:如何測試 react?

React 是一個超級流行的 JavaScript 庫,用於創建動態用戶界面。 Jest 可以順利地測試 React 應用(Jest 和 React 都來自 Facebook 的工程師)。 Jest 還是 Create React App 中的默認測試運行程序。

如果您想學習如何測試 React 組件,請查看 《測試 React 組件:最權威指南》 。該指南涵蓋了單元測試組件,類組件,帶有鈎子的功能組件以及新的 Act API。

結論(從這裏開始)

測試是一個巨大而有趣的話題。有許多類型的測試和許多測試庫。在本 Jest 教程中,你學習了如何配置 Jest 進行覆蓋率報告,如何組織和編寫簡單的單元測試以及如何測試 JavaScript 代碼。

要了解有關 UI 測試的更多信息,我強烈建議您看一下 Cypress 的 JavaScript 端到端測試 。

如果你準備好想入坑自動化測試和集成測試,那麼 在 js 中進行自動化測試和集成測試 會非常適合你。

您可以在 Github 上找到本教程的代碼: getting-started-with-jest 。

站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/7663