Jest 初學者教程:JavaScript 測試入門
測試的意義是什麼?
在程式設計術語中,測試意味著檢查我們的程式碼是否符合某些期望。例如:一個名為 “ transformer” 的函式應在給定某些輸入的情況下返回期望的輸出。
測試型別很多,但簡單來說測試分為三大類:
- 單元測試
- 整合測試
- UI 測試
在本 Jest 教程中,我們將僅介紹單元測試,但是在本文結尾,您將找到其他型別的測試的資源。
Jest 教程:什麼是 Jest
Jest 是JavaScript測試執行程式,即用於建立,執行和構建測試的JavaScript庫。Jest 是作為npm軟體包分發的,您可以將其安裝在任何 JavaScript 專案中。 Jest 是目前最受歡迎的測試執行程式之一(我覺得沒有之一),也是 CreatereactApp 的預設選擇。
首先,我怎麼知道要測試什麼?
在測試方面,即使是簡單的程式碼塊也可能使新手懵逼。最常見的問題是 “我怎麼知道要測試什麼?”。如果你正在編寫 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”
- 測試一個空的搜尋詞。函式應如何處理?
你將如何組織這些新測試?
在下一節中,我們將看到測試中的另一個重要主題:程式碼覆蓋率。
電腦刺繡繡花廠 http://www.szhdn.com 廣州品牌設計公司https://www.houdianzi.com
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。