在本页

行为驱动开发

使用 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/std@0.224.0/assert/mod.ts";
import {
  User,
} from "https://deno.land/std@0.224.0/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/std@0.224.0/assert/mod.ts";
import {
  afterEach,
  beforeEach,
  describe,
  it,
} from "https://deno.land/std@0.224.0/testing/bdd.ts";
import {
  User,
} from "https://deno.land/std@0.224.0/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/std@0.224.0/assert/mod.ts";
import { describe, it } from "https://deno.land/std@0.224.0/testing/bdd.ts";
import {
  User,
} from "https://deno.land/std@0.224.0/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/std@0.224.0/assert/mod.ts";
import { describe, it } from "https://deno.land/std@0.224.0/testing/bdd.ts";
import {
  User,
} from "https://deno.land/std@0.224.0/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);
  });
});