跳至主要内容

行为驱动开发

使用 bdd.ts 模块,您可以以熟悉的格式编写测试,用于对测试进行分组并添加其他 JavaScript 测试框架(如 Jasmine、Jest 和 Mocha)使用的设置/拆卸钩子。

describe 函数创建一个块,将多个相关的测试分组在一起。it 函数注册一个单独的测试用例。

钩子

测试套件中有 4 种类型的钩子可用。一个测试套件可以包含多种类型的钩子,它们将按照注册顺序被调用。afterEachafterAll 钩子将在测试用例通过或失败时被调用。*All 钩子将为整个组调用一次,而 *Each 钩子将为每个单独的测试用例调用一次。

  • beforeAll: 在测试套件中的所有测试之前运行。
  • afterAll: 在测试套件中的所有测试完成后运行。
  • beforeEach: 在测试套件中的每个单独的测试用例之前运行。
  • afterEach: 在测试套件中的每个单独的测试用例之后运行。

如果钩子在顶层注册,则将注册一个全局测试套件,所有测试都将属于它。在顶层注册的钩子必须在任何单独的测试用例或测试套件之前注册。

聚焦测试

如果您想只运行特定的测试用例,您可以通过调用 it.only 而不是 it 来实现。如果您想只运行特定的测试套件,您可以通过调用 describe.only 而不是 describe 来实现。

在使用扁平测试分组样式时,这有一个限制。当 describe 被调用而不被嵌套时,它会使用 Deno.test 注册测试。如果子测试用例或套件使用 it.onlydescribe.only 注册,它将被限定在顶层测试套件而不是文件。为了使它们成为文件中唯一运行的测试,您需要使用 describe.only 重新注册顶层测试套件。

忽略测试

如果您不想运行特定的单个测试用例,您可以通过调用 it.ignore 而不是 it 来实现。如果您不想运行特定的测试套件,您可以通过调用 describe.ignore 而不是 describe 来实现。

清理选项

Deno.TestDefinition 一样,DescribeDefinitionItDefinition 也有清理选项。它们的工作方式相同。

  • sanitizeExit: 确保测试用例不会过早地导致进程退出,例如通过调用 Deno.exit。默认为 true。
  • sanitizeOps: 检查测试后异步完成的操作数量是否与已分派的操作数量相同。默认为 true。
  • sanitizeResources: 确保测试用例不会“泄漏”资源 - 也就是说,测试后的资源表与测试前的资源表完全相同。默认为 true。

权限选项

Deno.TestDefinition 相似,DescribeDefinitionItDefinition 都有一个 permissions 选项。它们指定用于运行单个测试用例或测试套件的权限。将其设置为 "inherit" 以保留调用线程的权限。将其设置为 "none" 以撤销所有权限。

此设置默认为 "inherit"

目前对此有一个限制,您不能在属于另一个测试套件的单个测试用例或测试套件上使用权限选项。这是因为在内部,这些测试是使用 t.step 注册的,它不支持权限选项。

与 Deno.test 相比

编写测试的默认方式是使用 Deno.testt.stepdescribeit 函数具有与 Deno.test 相似的调用签名,这使得在默认样式和行为驱动开发样式的测试编写之间轻松切换。在内部,describeit 使用 Deno.testt.step 注册测试。

以下是一个使用 Deno.testt.step 的测试文件的示例。在接下来的部分中,将提供如何使用嵌套测试分组、扁平测试分组或两种样式的混合使用 describeit 编写相同测试的示例。

// https://deno.land/std/testing/bdd_examples/user_test.ts
import {
assertEquals,
assertStrictEquals,
assertThrows,
} from "https://deno.land/[email protected]/assert/mod.ts";
import {
User,
} from "https://deno.land/[email protected]/testing/bdd_examples/user.ts";

Deno.test("User.users initially empty", () => {
assertEquals(User.users.size, 0);
});

Deno.test("User constructor", () => {
try {
const user = new User("Kyle");
assertEquals(user.name, "Kyle");
assertStrictEquals(User.users.get("Kyle"), user);
} finally {
User.users.clear();
}
});

Deno.test("User age", async (t) => {
const user = new User("Kyle");

await t.step("getAge", () => {
assertThrows(() => user.getAge(), Error, "Age unknown");
user.age = 18;
assertEquals(user.getAge(), 18);
});

await t.step("setAge", () => {
user.setAge(18);
assertEquals(user.getAge(), 18);
});
});

嵌套测试分组

describe 函数调用回调中创建的测试将属于它创建的新测试套件。钩子可以在其中创建,也可以添加到 describe 的选项参数中。

// https://deno.land/std/testing/bdd_examples/user_nested_test.ts
import {
assertEquals,
assertStrictEquals,
assertThrows,
} from "https://deno.land/[email protected]/assert/mod.ts";
import {
afterEach,
beforeEach,
describe,
it,
} from "https://deno.land/[email protected]/testing/bdd.ts";
import {
User,
} from "https://deno.land/[email protected]/testing/bdd_examples/user.ts";

describe("User", () => {
it("users initially empty", () => {
assertEquals(User.users.size, 0);
});

it("constructor", () => {
try {
const user = new User("Kyle");
assertEquals(user.name, "Kyle");
assertStrictEquals(User.users.get("Kyle"), user);
} finally {
User.users.clear();
}
});

describe("age", () => {
let user: User;

beforeEach(() => {
user = new User("Kyle");
});

afterEach(() => {
User.users.clear();
});

it("getAge", function () {
assertThrows(() => user.getAge(), Error, "Age unknown");
user.age = 18;
assertEquals(user.getAge(), 18);
});

it("setAge", function () {
user.setAge(18);
assertEquals(user.getAge(), 18);
});
});
});

扁平测试分组

describe 函数返回一个唯一的符号,可用于引用测试套件,以便向其添加测试,而无需在回调中创建它们。这使您能够在没有分组测试前面额外的缩进的情况下进行测试分组。

// https://deno.land/std/testing/bdd_examples/user_flat_test.ts
import {
assertEquals,
assertStrictEquals,
assertThrows,
} from "https://deno.land/[email protected]/assert/mod.ts";
import {
describe,
it,
} from "https://deno.land/[email protected]/testing/bdd.ts";
import {
User,
} from "https://deno.land/[email protected]/testing/bdd_examples/user.ts";

const userTests = describe("User");

it(userTests, "users initially empty", () => {
assertEquals(User.users.size, 0);
});

it(userTests, "constructor", () => {
try {
const user = new User("Kyle");
assertEquals(user.name, "Kyle");
assertStrictEquals(User.users.get("Kyle"), user);
} finally {
User.users.clear();
}
});

const ageTests = describe({
name: "age",
suite: userTests,
beforeEach(this: { user: User }) {
this.user = new User("Kyle");
},
afterEach() {
User.users.clear();
},
});

it(ageTests, "getAge", function () {
const { user } = this;
assertThrows(() => user.getAge(), Error, "Age unknown");
user.age = 18;
assertEquals(user.getAge(), 18);
});

it(ageTests, "setAge", function () {
const { user } = this;
user.setAge(18);
assertEquals(user.getAge(), 18);
});

混合测试分组

嵌套测试分组和扁平测试分组可以一起使用。如果您想创建深度分组,但又不想在每行前面添加额外的缩进,这将非常有用。

// https://deno.land/std/testing/bdd_examples/user_mixed_test.ts
import {
assertEquals,
assertStrictEquals,
assertThrows,
} from "https://deno.land/[email protected]/assert/mod.ts";
import {
describe,
it,
} from "https://deno.land/[email protected]/testing/bdd.ts";
import {
User,
} from "https://deno.land/[email protected]/testing/bdd_examples/user.ts";

describe("User", () => {
it("users initially empty", () => {
assertEquals(User.users.size, 0);
});

it("constructor", () => {
try {
const user = new User("Kyle");
assertEquals(user.name, "Kyle");
assertStrictEquals(User.users.get("Kyle"), user);
} finally {
User.users.clear();
}
});

const ageTests = describe({
name: "age",
beforeEach(this: { user: User }) {
this.user = new User("Kyle");
},
afterEach() {
User.users.clear();
},
});

it(ageTests, "getAge", function () {
const { user } = this;
assertThrows(() => user.getAge(), Error, "Age unknown");
user.age = 18;
assertEquals(user.getAge(), 18);
});

it(ageTests, "setAge", function () {
const { user } = this;
user.setAge(18);
assertEquals(user.getAge(), 18);
});
});