agentsclimarketplace

Test driven development

Skill cjbpq/deep-reviewer-seek/.github/skills/test-driven-development

当实现任何功能或bug修复时,在编写实现代码之前使用From its SKILL.md

Install
npx -y skills add cjbpq/deep-reviewer-seek --skill test-driven-development

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

6.8 KB, as published. Nobody here has run it

测试驱动开发 (TDD)

概述

先写测试。看它失败。写最小代码让它通过。

核心原则: 如果你没看到测试失败,你就不知道它是否测试了正确的东西。

违反规则的字面意义就是违反其精神。

何时使用

始终:

  • 新功能
  • Bug修复
  • 重构
  • 行为变更

例外(询问你的主管):

  • 一次性原型
  • 生成代码
  • 配置文件

在想"就这一次跳过TDD"?停止。那是合理化。

铁律

无失败测试,不得写生产代码

在测试之前写了代码?删除它。重新开始。

无例外:

  • 不要保留作为"参考"
  • 不要在写测试时"适配"它
  • 不要看它
  • 删除意味着删除

从测试重新实现。句号。

红-绿-重构

红 — 写失败测试

写一个展示应该发生什么的最小测试。

好测试:

test('失败操作重试3次', async () => {
  let attempts = 0;
  const operation = () => {
    attempts++;
    if (attempts < 3) throw new Error('失败');
    return '成功';
  };

  const result = await retryOperation(operation);

  expect(result).toBe('成功');
  expect(attempts).toBe(3);
});

清晰名称,测试真实行为,一件事

坏测试:

test('重试有效', async () => {
  const mock = jest.fn()
    .mockRejectedValueOnce(new Error())
    .mockRejectedValueOnce(new Error())
    .mockResolvedValueOnce('成功');
  await retryOperation(mock);
  expect(mock).toHaveBeenCalledTimes(3);
});

模糊名称,测试mock而非代码

要求:

  • 一个行为
  • 清晰名称
  • 真实代码(除非不可避免,不用mock)

验证红 — 看它失败

强制。绝不跳过。

npm test path/to/test.test.ts

确认:

  • 测试失败(非错误)
  • 失败消息如预期
  • 失败因为功能缺失(非拼写错误)

测试通过了? 你在测试已有行为。修复测试。

测试报错了? 修复错误,重新运行直到它正确失败。

绿 — 最小代码

写最简单的代码让测试通过。

好:

async function retryOperation<T>(fn: () => Promise<T>): Promise<T> {
  for (let i = 0; i < 3; i++) {
    try {
      return await fn();
    } catch (e) {
      if (i === 2) throw e;
    }
  }
  throw new Error('不可达');
}

刚好够通过

坏:

async function retryOperation<T>(
  fn: () => Promise<T>,
  options?: {
    maxRetries?: number;
    backoff?: 'linear' | 'exponential';
    onRetry?: (attempt: number) => void;
  }
): Promise<T> {
  // YAGNI — 过度设计
}

不要添加功能、重构其他代码或"改进"超出测试范围的东西。

验证绿 — 看它通过

强制。

npm test path/to/test.test.ts

确认:

  • 测试通过
  • 其他测试仍然通过
  • 输出干净(无错误、警告)

测试失败? 修复代码,非测试。

其他测试失败? 现在修复。

重构 — 清理

仅在绿色之后:

  • 消除重复
  • 改进命名
  • 提取辅助函数

保持测试绿色。不添加行为。

重复

下一个功能的下一个失败测试。

好测试的标准

质量好坏
最小一件事。名字里有"和"?拆分。test('验证邮箱和域名和空白')
清晰名称描述行为test('test1')
展示意图演示期望的API掩盖代码应该做什么

为什么顺序重要

"我先实现,然后写测试验证它有效"

实现后写的测试会立即通过。立即通过证明不了任何事:

  • 可能测试了错误的东西
  • 可能测试了实现而非行为
  • 可能遗漏了你忘记的边缘情况
  • 你从未看到它捕获到bug

测试优先强制你看到测试失败,证明它确实测试了什么。

"我已经手动测试了所有边缘情况"

手动测试是临时的。你以为你测试了一切但是:

  • 没有你测试了什么的记录
  • 代码变更时不能重新运行
  • 压力下容易忘记情况
  • "我试的时候能工作" ≠ 全面

自动化测试是系统性的。它们每次以相同方式运行。

"删除X小时的工作是浪费"

沉没成本谬误。时间已经过去。你现在的选择:

  • 删除并用TDD重写(X小时,高信心)
  • 保留并在之后加测试(30分钟,低信心,可能有bug)

"浪费"是保留你无法信任的代码。没有真实测试的工作代码是技术债务。

常见合理化

借口现实
"太简单不需要测试"简单代码也会坏。测试只要30秒。
"我之后测试"立即通过的测试什么也证明不了。
"之后测试达到同样目标"之后测试="这做什么?" 测试优先="这应该做什么?"
"已经手动测试了"临时≠系统。无记录,不能重跑。
"删除X小时是浪费"沉没成本谬误。保留未验证代码是技术债务。
"作为参考保留,测试优先写"你会适配它。那是先实现后测试。删除就是删除。
"需要先探索"可以。扔掉探索代码,用TDD开始。
"测试困难=设计不清"听从测试。难测试=难使用。
"TDD会拖慢我"TDD比调试快。务实=测试优先。
"手动测试更快"手动不能证明边缘情况。每次变更你都要重测。

红线 — 停止并重新开始

  • 测试前写了代码
  • 实现后写测试
  • 测试立即通过
  • 无法解释测试为何失败
  • 测试"稍后"加
  • 合理化"就这一次"
  • "我已经手动测试了"
  • "保留作为参考"或"适配已有代码"
  • "已经花了X小时,删除是浪费"
  • "TDD是教条,我在务实"

所有这些意味着:删除代码。用TDD重新开始。

验证检查清单

标记工作完成之前:

  • 每个新函数/方法有测试
  • 看到每个测试在实现前失败
  • 每个测试因预期原因失败(功能缺失,非拼写)
  • 写了最小代码让每个测试通过
  • 所有测试通过
  • 输出干净(无错误、警告)
  • 测试使用真实代码(mock仅在不可避免时)
  • 边缘情况和错误被覆盖

不能检查所有项?你跳过了TDD。重新开始。

当卡住时

问题解决方案
不知道如何测试写期望API。先写断言。问你的主管。
测试太复杂设计太复杂。简化接口。
必须mock一切代码太耦合。使用依赖注入。
测试设置巨大提取辅助函数。还是复杂?简化设计。

调试集成

发现bug?写复现它的失败测试。遵循TDD循环。测试证明修复并防止回归。

绝对不要没有测试就修复bug。

最终规则

生产代码 → 测试存在且先失败
否则 → 不是TDD

无例外,除非主管允许。

Keep looking

Skills are one crate of 627,801. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.