es6 class 可测性关键在于合理设计:依赖需可注入、状态应可观察、行为须可隔离;构造函数接收依赖(如 api 客户端)、方法保持纯或可预测、暴露只读状态、用组合代替继承。

用 ES6 Class 写业务代码时,真正影响可测性的不是语法本身,而是类的设计方式。核心是让依赖可替换、状态可观察、行为可隔离。
把外部依赖抽成可注入的参数
避免在 class 内部直接 new 实例或调用全局函数。构造函数接收依赖(如 API 客户端、时间工具、事件总线),测试时传入 mock 对象即可。
- 错误写法:this.api = new ApiClient() —— 硬编码依赖,无法替换
- 正确写法:constructor(api, clock = Date) { this.api = api; this.clock = clock; }
- 测试时传入:new UserService(mockApi, { now: () => 1717027200000 })
方法尽量保持纯或可预测
业务逻辑方法不修改外部状态、不产生副作用,只处理输入并返回结果。副作用(如发请求、改 DOM)单独封装,便于 stub 或跳过。
- 把计算逻辑拆出来:calculateDiscount(items, user) 是纯函数,直接单元测试
- 把副作用方法标记为可覆盖:async fetchUserData() { return this.api.get('/user'); },测试中重写它返回固定数据
- 避免在 constructor 中执行异步操作或读取 localStorage —— 这会让实例化变不可控
暴露必要状态用于断言
测试常需验证内部状态是否按预期更新。合理暴露只读属性或 getter,而不是全靠私有字段加反射。
- 用 get loading() { return this._loading; } 替代直接访问 this._loading
- 状态变更后触发事件(如 this.emit('statusChanged', { type: 'success' })),测试监听事件比轮询更可靠
- 避免过度封装:如果 this.items 是公开且只读的数组,测试中直接断言长度和内容完全合理
用组合代替继承,降低耦合
继承会隐式携带父类行为和状态,增加测试复杂度。优先用组合 + 依赖注入,每个类职责单一、边界清晰。
- 不要写 class OrderService extends BaseService,而是 class OrderService { constructor(logger, validator) { ... } }
- 共用逻辑提取为独立工具类或函数,比如 formatCurrency、validateEmail,它们本身无状态、易测
- 测试 OrderService 时,只需 mock logger 和 validator,不用关心 BaseService 的生命周期或缓存策略











