← 中文资源

简体中文版本

链式测试数据构造器

通过分层覆盖、按序生成与重置能力构造可读的测试数据,减少重复对象字面量。

阅读英文原文

功能说明

该示例实现一个链式测试数据构造器。它先接收一组默认字段,之后可以多次 with 叠加覆盖,最后 build 出完整对象。buildList 能按数量生成一组数据,并允许根据下标为每条记录注入不同字段,适合构造用户、订单、文章等测试夹具,避免在每个用例里复制大段对象字面量。

关键代码

createBuilder 保存 defaults 与 layers 数组。build 使用 Object.assign 按默认值、各层覆盖、最后一次覆盖的顺序合并,后写入者优先。with 校验参数必须是普通对象后入栈并返回自身,从而支持链式调用。reset 清空覆盖层,便于不同测试之间复用同一个构造器。buildList 校验数量为非负整数,并把下标传给回调生成差异字段。

使用注意

合并是浅合并,嵌套对象会被整体替换而不是递归合并,需要深层差异时应使用深合并工具。layers 会持续累积,跨用例复用前应调用 reset。默认值中的数组或对象会被多个结果共享引用,测试中修改嵌套结构可能互相污染。构造器不会自动生成唯一 ID,需要唯一值时应在回调中显式赋值。

验证方式

断言 with 覆盖 role 后 build 得到 admin,同时保留默认 active。调用 reset 后再次 build 断言 role 回到 member。用 buildList 生成两条记录,断言 id 分别为 1 和 2 且互不共享顶层对象。测试负数数量、非函数回调与非对象覆盖分别抛错。

代码示例

export function createBuilder(defaults) {
  if (defaults === null || typeof defaults !== "object" || Array.isArray(defaults)) {
    throw new TypeError("defaults must be a plain object");
  }
  const layers = [];
  const build = (overrides = {}) => Object.assign({}, defaults, ...layers, overrides);
  const assertOverrides = (overrides) => {
    if (overrides === null || typeof overrides !== "object" || Array.isArray(overrides)) {
      throw new TypeError("overrides must be a plain object");
    }
  };
  const api = {
    with(overrides) {
      assertOverrides(overrides);
      layers.push(overrides);
      return api;
    },
    reset() {
      layers.length = 0;
      return api;
    },
    build,
    buildList(count, overrides = () => ({})) {
      if (!Number.isInteger(count) || count < 0) {
        throw new RangeError("count must be a non-negative integer");
      }
      if (typeof overrides !== "function") throw new TypeError("overrides must be a function");
      return Array.from({ length: count }, (_, index) => build(overrides(index)));
    },
  };
  return api;
}

const user = createBuilder({ role: "member", active: true });
console.log(user.with({ role: "admin" }).build({ name: "Ada" }));
console.log(user.reset().buildList(2, (index) => ({ id: index + 1 })));