为测试必填参数空输入编写单元测试,需容忍语法错误吗?
这问题问得太戳痛点了——我刚用TypeScript的时候也纠结过:明明IDE和编译阶段就会拦着无参调用,Kotlin这类语言甚至直接不让编译,那写那堆看起来多余的空值检查和对应的单元测试到底有啥意义?
其实得结合实际项目的运行场景来看,这些检查很多时候真的不能省:
1. 你的代码可能被纯JavaScript调用
TypeScript最终会编译成JavaScript运行,如果你的模块会被纯JS项目引用(比如给其他团队提供的工具库),那TypeScript的类型约束就彻底失效了。纯JS代码里完全可以不传参数直接调用getIngredient(),这时候如果没有runtime的空值检查,程序要么出现意外行为(比如后续逻辑用undefined做操作),要么直接崩掉——而提前抛出明确的错误,比让问题隐蔽发酵要好得多。
2. 类型断言/转换可能绕过静态检查
就算你自己的项目全是TypeScript,也难免会遇到开发者用any、as断言或者@ts-ignore跳过类型检查的情况,比如:
const maybeNull: string | null = null; p.getIngredient(maybeNull as string); // 类型断言直接绕过了静态检查
这种情况下,TypeScript拦不住,只能靠runtime的空值检查来兜底,避免null/undefined流入后续逻辑。
3. 外部数据可能突破类型定义
如果你的函数参数来自API响应、本地存储或者用户输入,就算你用TypeScript定义了类型,实际运行时拿到的数据也可能不符合预期(比如后端漏传字段、本地存储的数据被篡改)。静态类型检查没法在运行时验证这些数据的合法性,这时候runtime的空值检查就能及时抛出错误,避免更隐蔽的bug。
4. 单元测试是在验证runtime的防御逻辑
对应的单元测试不是在测试TypeScript的类型系统——那是编译器该做的事——而是在验证当意外情况发生时,你的runtime防御逻辑能正常工作。测试空输入的情况,能确保程序会按照预期抛出明确的错误,而不是静默失败或者产生奇怪的结果,这在排查生产问题时能节省很多时间。
当然,如果你的项目是完全封闭的:所有调用都是严格的TypeScript,不会被外部JS引用,也不会有类型绕过、外部数据流入的情况,那这些检查确实有点冗余。但大部分实际项目很难做到这么“纯净”,所以保留这些防御性检查和测试,相当于给程序加了一层安全网,避免在生产环境因为一些意料之外的情况掉链子。
内容的提问来源于stack exchange,提问作者jchtd

