deno.com
本頁面內容

測試

Deno 提供了一個內建的測試執行器,用於在 JavaScript 和 TypeScript 中編寫和執行測試。這使得確保您的程式碼可靠且如預期運作變得容易,而無需安裝任何額外的依賴項或工具。deno test 執行器允許您對每個測試的權限進行細緻的控制,確保程式碼不會執行任何意外的操作。

除了內建的測試執行器之外,您還可以使用來自 JS 生態系統的其他測試執行器,例如 Jest、Mocha 或 AVA 與 Deno。但是,我們不會在本文檔中涵蓋這些內容。

編寫測試 跳到標題

若要在 Deno 中定義測試,您可以使用 Deno.test() 函數。以下是一些範例

my_test.ts
import { assertEquals } from "jsr:@std/assert";

Deno.test("simple test", () => {
  const x = 1 + 2;
  assertEquals(x, 3);
});

import { delay } from "jsr:@std/async";

Deno.test("async test", async () => {
  const x = 1 + 2;
  await delay(100);
  assertEquals(x, 3);
});

Deno.test({
  name: "read file test",
  permissions: { read: true },
  fn: () => {
    const data = Deno.readTextFileSync("./somefile.txt");
    assertEquals(data, "expected content");
  },
});

如果您偏好「類似 jest」的 expect 斷言樣式,Deno 標準函式庫提供了一個 expect 函數,可以用來取代 assertEquals

my_test.ts
import { expect } from "jsr:@std/expect";
import { add } from "./add.js";

Deno.test("add function adds two numbers correctly", () => {
  const result = add(2, 3);
  expect(result).toBe(5);
});

執行測試 跳到標題

若要執行您的測試,請使用 deno test 子命令。

如果執行時沒有檔案名稱或目錄名稱,此子命令將自動尋找並執行目前目錄(遞迴地)中所有符合 glob {*_,*.,}test.{ts, tsx, mts, js, mjs, jsx} 的測試。

# Run all tests in the current directory and all sub-directories
deno test

# Run all tests in the util directory
deno test util/

# Run just my_test.ts
deno test my_test.ts

# Run test modules in parallel
deno test --parallel

# Pass additional arguments to the test file that are visible in `Deno.args`
deno test my_test.ts -- -e --foo --bar

# Provide permission for deno to read from the filesystem, which is necessary
# for the final test above to pass
deno test --allow-read my_test.ts

測試步驟 跳到標題

Deno 也支援測試步驟,這讓您可以將測試分解為更小、更易於管理的部分。這對於測試內的設定和拆卸操作很有用

Deno.test("database operations", async (t) => {
  using db = await openDatabase();
  await t.step("insert user", async () => {
    // Insert user logic
  });
  await t.step("insert book", async () => {
    // Insert book logic
  });
});

命令列篩選 跳到標題

Deno 允許您使用命令列上的 --filter 選項來執行特定的測試或測試群組。此選項接受字串或模式來比對測試名稱。篩選不會影響步驟;如果測試名稱符合篩選條件,則會執行其所有步驟。

考量以下測試

Deno.test("my-test", () => {});
Deno.test("test-1", () => {});
Deno.test("test-2", () => {});

依字串篩選 跳到標題

若要執行名稱中包含「my」一詞的所有測試,請使用

deno test --filter "my" tests/

此命令將執行 my-test,因為它包含「my」一詞。

依模式篩選 跳到標題

若要執行符合特定模式的測試,請使用

deno test --filter "/test-*\d/" tests/

此命令將執行 test-1test-2,因為它們符合模式 test-* 後面接著一個數字。

若要指示您正在使用模式(正規表示式),請使用正斜線 / 包裝您的篩選值,就像 JavaScript 的正規表示式語法一樣。

在設定檔中包含和排除測試檔案 跳到標題

您也可以透過在 Deno 設定檔中指定要包含或排除的路徑來篩選測試。

例如,如果您只想測試 src/fetch_test.tssrc/signal_test.ts 並排除 out/ 中的所有內容

{
  "test": {
    "include": [
      "src/fetch_test.ts",
      "src/signal_test.ts"
    ]
  }
}

或更可能是

{
  "test": {
    "exclude": ["out/"]
  }
}

測試定義選擇 跳到標題

Deno 提供了兩個選項,用於在測試定義本身中選擇測試:忽略測試和專注於特定測試。

忽略/跳過測試 跳到標題

您可以根據特定條件使用測試定義中的 ignore 布林值來忽略某些測試。如果 ignore 設定為 true,則會跳過測試。例如,如果您只想在特定作業系統上執行測試,這會很有用。

Deno.test({
  name: "do macOS feature",
  ignore: Deno.build.os !== "darwin", // This test will be ignored if not running on macOS
  fn() {
    // do MacOS feature here
  },
});

如果您想要忽略測試而不傳遞任何條件,您可以使用 Deno.test 物件中的 ignore() 函數

Deno.test.ignore("my test", () => {
  // your test code
});

僅執行特定測試 跳到標題

如果您想專注於特定測試並忽略其餘測試,您可以使用 only 選項。這會告知測試執行器僅執行將 only 設定為 true 的測試。多個測試可以設定此選項。但是,如果任何測試標記為 only,則整體測試執行將始終失敗,因為這旨在作為偵錯的臨時措施。

Deno.test.only("my test", () => {
  // some test code
});

Deno.test({
  name: "Focus on this test only",
  only: true, // Only this test will run
  fn() {
    // test complicated stuff here
  },
});

快速失敗 跳到標題

如果您有長時間執行的測試套件,並希望在第一次失敗時停止,您可以在執行套件時指定 --fail-fast 標誌。

deno test --fail-fast

這將導致測試執行器在第一次測試失敗後停止執行。

報告器 跳到標題

Deno 包含三個內建報告器來格式化測試輸出

  • pretty(預設):提供詳細且易於閱讀的輸出。
  • dot:提供簡潔的輸出,對於快速查看測試結果很有用。
  • junit:以 JUnit XML 格式產生輸出,這對於與 CI/CD 工具整合很有用。

您可以使用 --reporter 標誌指定要使用的報告器

# Use the default pretty reporter
deno test

# Use the dot reporter for concise output
deno test --reporter=dot

# Use the JUnit reporter
deno test --reporter=junit

此外,您可以將 JUnit 報告寫入檔案,同時仍然在終端機中獲得人類可讀的輸出,方法是使用 --junit-path 標誌

deno test --junit-path=./report.xml

間諜、模擬(測試替身)、樁和偽造時間 跳到標題

Deno 標準函式庫提供了一組函數,可協助您編寫涉及間諜、模擬和樁的測試。請查看 JSR 上的 @std/testing 文件,以取得有關這些工具的更多資訊。

覆蓋率 跳到標題

如果您在啟動 deno test 時指定 --coverage 標誌,Deno 將會收集測試覆蓋率到您程式碼的目錄中。此覆蓋率資訊直接從 V8 JavaScript 引擎取得,確保高準確性。

然後可以使用 deno coverage 工具將其從內部格式進一步處理為眾所周知的格式,例如 lcov

行為驅動開發 跳到標題

透過 @std/testing/bdd 模組,您可以使用熟悉的格式編寫測試,以用於分組測試和新增其他 JavaScript 測試框架(如 Jasmine、Jest 和 Mocha)使用的設定/拆卸掛鉤。

describe 函數建立一個區塊,將多個相關測試分組在一起。it 函數註冊個別的測試案例。例如

import { describe, it } from "jsr:@std/testing/bdd";
import { expect } from "jsr:@std/expect";
import { add } from "./add.js";

describe("add function", () => {
  it("adds two numbers correctly", () => {
    const result = add(2, 3);
    expect(result).toBe(5);
  });

  it("handles negative numbers", () => {
    const result = add(-2, -3);
    expect(result).toBe(-5);
  });
});

請查看 JSR 上的文件,以取得有關這些函數和掛鉤的更多資訊。

文件測試 跳到標題

Deno 允許您評估以 JSDoc 或 markdown 檔案撰寫的程式碼片段。這可確保您文件中的範例是最新的且可運作的。

範例程式碼區塊 跳到標題

example.ts
/**
 * # Examples
 *
 * ```ts
 * import { assertEquals } from "jsr:@std/assert/equals";
 *
 * const sum = add(1, 2);
 * assertEquals(sum, 3);
 * ```
 */
export function add(a: number, b: number): number {
  return a + b;
}

三個反引號標記程式碼區塊的開始和結束,語言由語言識別符屬性決定,它可以是以下其中之一

  • js
  • javascript
  • mjs
  • cjs
  • jsx
  • ts
  • typescript
  • mts
  • cts
  • tsx

如果未指定語言識別符,則語言會從程式碼區塊擷取來源文件媒體類型推斷。

deno test --doc example.ts

上述命令將擷取此範例,將其轉換為如下所示的虛擬測試案例

example.ts$4-10.ts
import { assertEquals } from "jsr:@std/assert/equals";
import { add } from "file:///path/to/example.ts";

Deno.test("example.ts$4-10.ts", async () => {
  const sum = add(1, 2);
  assertEquals(sum, 3);
});

然後將其作為獨立模組在與文件模組相同的目錄中執行。

只想進行類型檢查?

如果您只想在 JSDoc 和 markdown 檔案中類型檢查您的程式碼片段,而無需實際執行它們,則可以使用 deno check 命令,並搭配 --doc 選項(用於 JSDoc)或 --doc-only 選項(用於 markdown)來代替。

匯出的項目會自動匯入 跳到標題

查看上面產生的測試程式碼,您會注意到它包含 import 語句來匯入 add 函數,即使原始程式碼區塊沒有它。在文件化模組時,從模組匯出的任何項目都會使用相同的名稱自動包含在產生的測試程式碼中。

假設我們有以下模組

example.ts
/**
 * # Examples
 *
 * ```ts
 * import { assertEquals } from "jsr:@std/assert/equals";
 *
 * const sum = add(ONE, getTwo());
 * assertEquals(sum, 3);
 * ```
 */
export function add(a: number, b: number): number {
  return a + b;
}

export const ONE = 1;
export default function getTwo() {
  return 2;
}

這將轉換為以下測試案例

example.ts$4-10.ts
import { assertEquals } from "jsr:@std/assert/equals";
import { add, ONE }, getTwo from "file:///path/to/example.ts";

Deno.test("example.ts$4-10.ts", async () => {
  const sum = add(ONE, getTwo());
  assertEquals(sum, 3);
});

跳過程式碼區塊 跳到標題

您可以透過新增 ignore 屬性來跳過程式碼區塊的評估。

/**
 * This code block will not be run.
 *
 * ```ts ignore
 * await sendEmail("deno@example.com");
 * ```
 */
export async function sendEmail(to: string) {
  // send an email to the given address...
}

清理器 跳到標題

測試執行器提供多個清理器,以確保測試以合理且預期的方式運作。

資源清理器 跳到標題

資源清理器確保在測試期間建立的所有 I/O 資源都已關閉,以防止洩漏。

I/O 資源是指 Deno.FsFile 句柄、網路連線、fetch 主體、計時器和其他不會自動進行垃圾回收的資源。

您應該始終在完成資源後關閉它們。例如,若要關閉檔案

const file = await Deno.open("hello.txt");
// Do something with the file
file.close(); // <- Always close the file when you are done with it

若要關閉網路連線

const conn = await Deno.connect({ hostname: "example.com", port: 80 });
// Do something with the connection
conn.close(); // <- Always close the connection when you are done with it

若要關閉 fetch 主體

const response = await fetch("https://example.com");
// Do something with the response
await response.body?.cancel(); // <- Always cancel the body when you are done with it, if you didn't consume it otherwise

此清理器預設為啟用,但可以使用 sanitizeResources: false 在此測試中停用

Deno.test({
  name: "leaky resource test",
  async fn() {
    await Deno.open("hello.txt");
  },
  sanitizeResources: false,
});

非同步操作清理器 跳到標題

非同步操作清理器確保在測試中啟動的所有非同步操作在測試結束前完成。這很重要,因為如果非同步操作未被等待,測試將在操作完成之前結束,並且即使操作實際上可能已失敗,測試也將被標記為成功。

您應該始終在測試中等待所有非同步操作。例如

Deno.test({
  name: "async operation test",
  async fn() {
    await new Promise((resolve) => setTimeout(resolve, 1000));
  },
});

此清理器預設為啟用,但可以使用 sanitizeOps: false 停用

Deno.test({
  name: "leaky operation test",
  fn() {
    crypto.subtle.digest(
      "SHA-256",
      new TextEncoder().encode("a".repeat(100000000)),
    );
  },
  sanitizeOps: false,
});

結束清理器 跳到標題

結束清理器確保受測程式碼不會呼叫 Deno.exit(),這可能會發出錯誤的測試成功訊號。

此清理器預設為啟用,但可以使用 sanitizeExit: false 停用。

Deno.test({
  name: "false success",
  fn() {
    Deno.exit(0);
  },
  sanitizeExit: false,
});

// This test never runs, because the process exits during "false success" test
Deno.test({
  name: "failing test",
  fn() {
    throw new Error("this test fails");
  },
});

快照測試 跳到標題

Deno 標準函式庫包含一個 快照模組,允許開發人員透過將值與參考快照進行比較來編寫測試。這些快照是原始值的序列化表示形式,並與測試檔案一起儲存。

快照測試能夠以極少的程式碼捕獲各種錯誤。在難以精確表達應該斷言什麼,而不需要過多的程式碼,或者測試進行的斷言預期經常變更的情況下,它特別有幫助。

您找到需要的資訊了嗎?

隱私權政策