在本页中
测试
Deno 提供了一个内置的测试运行器,用于在 JavaScript 和 TypeScript 中编写和运行测试。这使得轻松确保您的代码可靠且按预期运行,而无需安装任何额外的依赖项或工具。deno test
运行器允许您对每个测试的权限进行细粒度控制,确保代码不会执行任何意外操作。
除了内置的测试运行器之外,您还可以将 JS 生态系统中的其他测试运行器(例如 Jest、Mocha 或 AVA)与 Deno 一起使用。但是,我们不会在本文档中介绍这些内容。
编写测试 跳转到标题
要在 Deno 中定义测试,您可以使用 Deno.test()
函数。以下是一些示例
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
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-1
和 test-2
,因为它们与模式 test-*
后跟一位数字匹配。
要指示您正在使用模式(正则表达式),请用正斜杠 /
包裹您的过滤器值,就像 JavaScript 的正则表达式语法一样。
在配置文件中包含和排除测试文件 跳转到标题
您还可以通过在 Deno 配置文件中指定要包含或排除的路径来过滤测试。
例如,如果您只想测试 src/fetch_test.ts
和 src/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-path
标志将 JUnit 报告写入文件,同时仍然在终端中获得人类可读的输出
deno test --junit-path=./report.xml
间谍、模拟(测试替身)、桩和伪造时间 跳转到标题
Deno 标准库提供了一组函数来帮助您编写涉及间谍、模拟和桩的测试。查看 @std/testing JSR 文档以获取有关这些实用程序的更多信息。
覆盖率 跳转到标题
如果您在启动 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 文件中编写的代码片段。这确保了您的文档中的示例是最新的且功能齐全的。
示例代码块 跳转到标题
/**
* # 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
上面的命令将提取此示例,将其转换为如下所示的伪测试用例
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
函数,即使原始代码块中没有它。在记录模块时,从模块导出的任何项都会使用相同的名称自动包含在生成的测试代码中。
假设我们有以下模块
/**
* # 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;
}
这将转换为以下测试用例
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("[email protected]");
* ```
*/
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 标准库包含一个 快照模块,允许开发人员通过将值与参考快照进行比较来编写测试。这些快照是原始值的序列化表示形式,并与测试文件一起存储。
快照测试使您能够以极少的代码捕获各种错误。它在难以精确表达应该断言的内容,而无需大量代码,或者测试进行的断言预计会经常更改的情况下特别有用。