浏览 AI使用教程

重构前怎样固定现有行为,避免顺手改变业务逻辑

重构的前提是知道旧代码现在做什么。先用特征测试固定关键输入输出,再拆结构;业务规则要不要改,应当作为另一项明确决定。

沿完整工作流推进

前一阶段的可检查结果,是下一阶段的输入;中间证据不足时停在当前阶段。

  1. 01边重构边修业务逻辑,最后很难分清是哪一步改坏了
  2. 02先确认这次任务的起点、边界和交付
  3. 03从“界定重构目标”走到“比较前后证据”
  4. 04同一异常的两种任务
  5. 05完成后的特征测试与前后差异报告
沿同一任务走到交付

边重构边修业务逻辑,最后很难分清是哪一步改坏了

准备重构折扣计算,代码没有测试;函数混合会员等级、优惠券和日期规则。开发者一边拆函数一边修“看起来不合理”的结果,业务行为悄悄改变。

这是可执行的教学函数,不是 12 个生产订单的实际运行报告。

  • 已知异常也有执行中的断言
  • 结构重构未混入业务修复
  • 未覆盖行为明确列出
本例材料

先确认这次任务的起点、边界和交付

需要确认本例内容
现有材料Node.js 20+;price.mjs 和 price.test.mjs。冻结普通价、会员折扣及已知负数输入行为,不涉及数据库或外部副作用。
不能越过的边界先用特征测试固定当前可观察行为;已知异常单独记录,不在重构中顺手修;前后输出逐项比较。
要交付的结果特征测试与前后差异报告
处理记录

从“界定重构目标”走到“比较前后证据”

当前阶段实际处理
界定重构目标写要改善的结构、可维护性或性能问题,以及明确不改变的外部行为。
捕获当前行为为关键输入、边界、错误和副作用添加特征测试,哪怕当前结果不理想也先记录。
建立安全小步一次只移动、重命名或抽取一种结构,保持每步可编译、可测试和可审查。
分开业务修复发现真实 Bug 时单独记录和提交,先确认期望行为与负责人,不藏在重构中。
比较前后证据运行相同测试、接口样例和性能基线,审查 diff 是否只涉及目标范围。
可运行特征测试

把负数现状也锁住,不能跳过

在空目录保存以下两个文件,先运行 node --test。这里允许负数是故意保留的旧行为,不是业务建议。

price.mjs

重构前

export function total(cents, member) {
  if (member) return Math.round(cents * 0.9);
  return cents;
}
price.test.mjs

同一组保护测试

import test from 'node:test';
import assert from 'node:assert/strict';
import { total } from './price.mjs';
test('普通价', () => assert.equal(total(100, false), 100));
test('会员舍入', () => assert.equal(total(105, true), 95));
test('已知旧行为 ISSUE-NEGATIVE:负数仍参与折扣', () => {
  assert.equal(total(-100, true), -90);
});
price.mjs

重构后替换为以下内容,再运行 node --test

const memberPrice = cents => Math.round(cents * 0.9);
export function total(cents, member) {
  return member ? memberPrice(cents) : cents;
}

预期两版都是 3 条通过。若把负数测试改为 skip,重构引入 0、NaN 或异常都可能漏掉。这个小样没有覆盖类型转换、超大整数和业务税费,不能作为真实价格引擎的完整测试集。

同一异常的两种任务

任务输入 -100,true 的断言合并依据
本次结构重构保持 -90,断言实际执行前后可观察结果一致
后续业务修复业务确认后改为拒绝负数新契约、迁移影响与独立修复记录
最终输出

完成后的特征测试与前后差异报告

预期:重构前后同一组 3 条断言通过,包含当前负数返回 -90 的已知异常。若业务决定以后拒绝负数,在独立修复中修改契约和测试,不能在结构重构时 skip。

常见失败

为什么“前后结果一致,仍可能遗漏重要行为”还不能交付

前后结果一致,仍可能遗漏重要行为

原因
样本只来自正常订单
怎么改
补生产边界、错误和历史回归案例,并记录覆盖不到的外部副作用
验收方式

特征测试与前后差异报告通过哪些检查才算完成

依据与范围

本页实际使用的资料