跳到主要内容

断言

为了帮助开发人员编写测试,Deno 标准库附带了一个内置的 断言模块,可以从 https://deno.land/std/assert/mod.ts 导入。

import { assert } from "https://deno.land/std@0.208.0/assert/mod.ts";

Deno.test("你好,测试", () => {
assert("你好");
});

⚠️ 一些流行的断言库,比如 Chai,也可以在 Deno 中使用,例如用法请参见 https://deno.land/std/testing/chai_example.ts。

断言模块提供了 14 个断言:

  • assert(expr: unknown, msg = ""): asserts expr
  • assertEquals(actual: unknown, expected: unknown, msg?: string): void
  • assertExists(actual: unknown, msg?: string): void
  • assertNotEquals(actual: unknown, expected: unknown, msg?: string): void
  • assertStrictEquals(actual: unknown, expected: unknown, msg?: string): void
  • assertAlmostEquals(actual: number, expected: number, epsilon = 1e-7, msg?: string): void
  • assertInstanceOf(actual: unknown, expectedType: unknown, msg?: string): void
  • assertStringIncludes(actual: string, expected: string, msg?: string): void
  • assertArrayIncludes(actual: unknown[], expected: unknown[], msg?: string): void
  • assertMatch(actual: string, expected: RegExp, msg?: string): void
  • assertNotMatch(actual: string, expected: RegExp, msg?: string): void
  • assertObjectMatch( actual: Record<PropertyKey, unknown>, expected: Record<PropertyKey, unknown>): void
  • assertThrows(fn: () => void, ErrorClass?: Constructor, msgIncludes?: string | undefined, msg?: string | undefined): Error
  • assertRejects(fn: () => Promise<unknown>, ErrorClass?: Constructor, msgIncludes?: string | undefined, msg?: string | undefined): Promise<void>

除了上述断言,还有 快照模块 还公开了一个 assertSnapshot 函数。该模块的文档可以在 这里 找到。

断言

assert 方法是一个简单的 "真值" 断言,可用于断言任何可以推断为真的值。

Deno.test("测试 Assert", () => {
assert(1);
assert("你好");
assert(true);
});

存在

assertExists 可以用于检查值是否不为 nullundefined

assertExists("Deno");
Deno.test("测试 Assert Exists", () => {
assertExists("Deno");
assertExists(false);
assertExists(0);
});

相等性

有三种相等性断言可用,assertEquals()assertNotEquals()assertStrictEquals()

assertEquals()assertNotEquals() 方法提供了通用的相等性检查,能够断言基本类型和对象之间的相等性。

Deno.test("测试 Assert Equals", () => {
assertEquals(1, 1);
assertEquals("你好", "你好");
assertEquals(true, true);
assertEquals(undefined, undefined);
assertEquals(null, null);
assertEquals(new Date(), new Date());
assertEquals(new RegExp("abc"), new RegExp("abc"));

class Foo {}
const foo1 = new Foo();
const foo2 = new Foo();

assertEquals(foo1, foo2);
});

Deno.test("测试 Assert Not Equals", () => {
assertNotEquals(1, 2);
assertNotEquals("你好", "世界");
assertNotEquals(true, false);
assertNotEquals(undefined, "");
assertNotEquals(new Date(), Date.now());
assertNotEquals(new RegExp("abc"), new RegExp("def"));
});

相比之下,assertStrictEquals() 提供了一个更简单、更严格的相等性检查,基于 === 运算符。因此,它不会断言两个相同对象的实例,因为它们不会引用相同的对象。

Deno.test("测试 Assert Strict Equals", () => {
assertStrictEquals(1, 1);
assertStrictEquals("你好", "你好");
assertStrictEquals(true, true);
assertStrictEquals(undefined, undefined);
});

assertStrictEquals() 断言最适合用于希望进行精确检查的两种基本类型。

数字的相等性

在测试数字之间的相等性时,重要的是要记住,有些数字不能准确地表示为 IEEE-754 双精度浮点表示。

这在处理十进制数时尤为真,其中 assertStrictEquals() 在某些情况下可以工作,但在其他情况下不行:

import {
assertStrictEquals,
assertThrows,
} from "https://deno.land/std@0.208.0/assert/mod.ts";

Deno.test("测试带有浮点数的 Assert Strict Equals", () => {
assertStrictEquals(0.25 + 0.25, 0.25);
assertThrows(() => assertStrictEquals(0.1 + 0.2, 0.3));
//0.1 + 0.2 将被存储为 0.30000000000000004,而不是 0.3
});

相反,assertAlmostEquals() 提供了一种测试给定数字是否足够接近被视为相等的方法。默认容差设置为 1e-7,但也可以通过传递第三个可选参数来更改它。

import {
assertAlmostEquals,
assertThrows,
} from "https://deno.land/std@0.208.0/assert/mod.ts";

Deno.test("测试 Assert Almost Equals", () => {
assertAlmostEquals(0.1 + 0.2, 0.3);
assertAlmostEquals(0.1 + 0.2, 0.3, 1e-16);
assertThrows(() => assertAlmostEquals(0.1 + 0.2, 0.3, 1e-17));
});

实例类型

要检查对象是否是特定构造函数的实例,您可以使用 assertInstanceOf()。这有一个附加的好处,它让 TypeScript 知道传入的变量具有特定类型:

import { assertInstanceOf } from "https://deno.land/std@0.208.0/assert/mod.ts";

Deno.test("测试断言实例类型", () => {
const variable = new Date() as unknown;

assertInstanceOf(variable, Date);

// 现在不会引发类型错误,因为类型已经被断言。
variable.getDay();
});

包含

有两种方法可用于断言一个值是否包含一个值,assertStringIncludes()assertArrayIncludes()

assertStringIncludes() 断言对字符串执行简单的包含检查,以查看它是否包含期望的字符串。

Deno.test("测试断言字符串包含", () => {
assertStringIncludes("Hello World", "Hello");
});

assertArrayIncludes() 断言略微复杂一些,可以在数组中找到一个值以及一个数组中的值。

Deno.test("测试断言数组包含", () => {
assertArrayIncludes([1, 2, 3], [1]);
assertArrayIncludes([1, 2, 3], [1, 2]);
assertArrayIncludes(Array.from("Hello World"), Array.from("Hello"));
});

正则表达式

您可以通过 assertMatch()assertNotMatch() 断言来断言正则表达式。

Deno.test("测试断言匹配", () => {
assertMatch("abcdefghi", new RegExp("def"));

const basicUrl = new RegExp("^https?://[a-z.]+.com$");
assertMatch("https://www.google.com", basicUrl);
assertMatch("http://facebook.com", basicUrl);
});

Deno.test("测试断言不匹配", () => {
assertNotMatch("abcdefghi", new RegExp("jkl"));

const basicUrl = new RegExp("^https?://[a-z.]+.com$");
assertNotMatch("https://deno.land/", basicUrl);
});

对象

使用 assertObjectMatch 来检查 JavaScript 对象是否与对象的属性子集匹配。

// 简单子集
assertObjectMatch(
{ foo: true, bar: false },
{
foo: true,
},
);

异常

在 Deno 中,有两种方法来断言是否引发了错误,assertThrows()assertRejects()。这两个断言允许您检查是否已抛出错误,抛出的错误类型以及消息内容。

这两个断言之间的区别在于 assertThrows() 接受一个标准函数,而 assertRejects() 接受一个返回 Promise 的函数。

assertThrows() 断言将检查是否已抛出错误,并可选择检查抛出的错误是否是正确类型,并断言错误消息是否符合预期。

Deno.test("测试断言引发错误", () => {
assertThrows(
() => {
throw new Error("Panic!");
},
Error,
"Panic!",
);
});

assertRejects() 断言略微复杂,主要是因为它涉及到 Promise。但基本上它会捕获 Promise 中抛出的错误或拒绝。您还可以选择检查错误类型和错误消息。这类似于使用异步函数的 assertThrows()

Deno.test("测试断言异步引发错误", async () => {
await assertRejects(
() => {
return new Promise(() => {
throw new Error("Panic! Threw Error");
});
},
Error,
"Panic! Threw Error",
);

await assertRejects(
() => {
return Promise.reject(new Error("Panic! Reject Error"));
},
Error,
"Panic! Reject Error",
);
});

自定义消息

Deno 的每个内置断言都允许您覆盖标准的 CLI 错误消息,如果您愿意的话。例如,此示例将输出 "Values Don't Match!" 而不是标准的 CLI 错误消息。

Deno.test("测试断言相等失败自定义消息", () => {
assertEquals(1, 2, "Values Don't Match!");
});

自定义测试

尽管 Deno 提供了强大的 assertions 模块,但总有一些特定于项目的东西可以添加。创建 自定义断言函数 可以提高可读性并减少代码量。

import { AssertionError } from "https://deno.land/std@0.208.0/assert/mod.ts";

function assertPowerOf(actual: number, expected: number, msg?: string): void {
let received = actual;
while (received % expected === 0) received = received / expected;
if (received !== 1) {
if (!msg) {
msg = `actual: "${actual}" expected to be a power of : "${expected}"`;
}
throw new AssertionError(msg);
}
}

在您的代码中使用此匹配器如下:

Deno.test("测试断言幂次方", () => {
assertPowerOf(8, 2);
assertPowerOf(11, 4);
});