在Deno应用代码中内嵌测试用例:长期使用有哪些问题?
内嵌单元测试的写法:真实优劣势与长期影响
首先得承认,你说的这种内嵌测试的爽点完全是真实的——这种写法在特定场景下效率确实高,没有绝对的对错:
- 不需要在
auth.ts和auth_test.ts两个文件之间反复跳转,改完业务逻辑顺手就能在同一个文件里补完对应测试,省去了来回切窗口的麻烦 - 测试代码可以直接访问文件里的非导出私有函数(比如你示例里的
passMatch、genToken),不用为了测试特意把私有函数对外导出,确实能减少不少为了测试写的依赖注入、mock的额外封装工作 - 小体量单文件项目里,这种写法完全没有过度设计的问题,开发体验非常流畅
长期坚持这种写法的实际问题
很多人觉得这种写法不够规范,不是因为它开发阶段不好用,而是长期迭代下来的隐性成本很高:
1. 生产构建的额外负担
现在你觉得运行完全正常,是因为还在本地开发阶段,没有碰到生产打包的环节。等项目上线需要打生产包的时候,你没法直接把混了测试逻辑的业务代码直接丢给打包器——文件里不仅有测试用例,还引入了测试断言库这类仅开发阶段需要的依赖。
哪怕你想了办法绕过这个问题,不管是用正则把测试段从代码里挖出来,还是加环境判断只有测试运行时才加载测试代码,本质上都是给业务代码加了额外的复杂度:
- 一旦正则匹配的规则没覆盖全,不小心把测试代码、测试依赖打进生产包,不仅会平白增大包体积,甚至可能出现测试逻辑意外执行的线上故障
- 加环境判断的写法,会在纯业务代码里混入测试相关的条件分支,破坏业务逻辑的纯粹性,后续读代码的时候要额外分辨哪些是线上跑的逻辑,哪些是测试才用的逻辑
2. 团队协作下的维护混乱
等项目规模变大、团队人员变多之后,一个原本200行的业务文件,慢慢会因为不断补测试用例变成近千行的长文件:
- 新接手的同事要在大段测试代码、测试mock数据之间反复跳转找核心业务逻辑,反而比外置测试的模式更费时间
- 测试代码和业务代码在同一个作用域下,很容易出现测试之间的全局变量污染问题——比如某个测试临时改了文件顶层的变量,导致同文件里其他测试用例执行结果不稳定,排查这种“不稳定测试”的成本会非常高
- 后续如果要统一调整测试框架、修改测试的公共断言逻辑,你得在所有业务文件里挨个改测试代码,完全没有集中维护测试的便利性
3. 模块设计的惰性倒退
你现在觉得“少写依赖注入很方便”,但长期下来会悄悄拉低模块的设计质量:
- 因为内嵌测试可以直接访问私有函数,你会慢慢习惯把逻辑拆得很碎但不对外导出,短时间写代码确实快,但等其他模块想复用这些逻辑的时候,就得反复改导出权限,甚至干脆复制一份代码过去,产生大量重复的逻辑冗余
- 外置测试的模式其实是个强制设计校验——你要写外置测试,就会逼着自己把模块的对外接口设计得足够清晰,不需要依赖内部实现细节就能调用测试。而内嵌测试的模式下,你很容易写出绑死内部实现的模块,后续哪怕只是重构内部的函数名,所有内嵌的测试用例都得跟着改,重构的成本反而比测试外置的场景高很多
这种写法的合理使用边界
不是说内嵌测试完全不能用,关键是搞清楚适配的场景:
- 个人开发的小脚本、单文件工具、快速验证想法的原型项目,完全可以这么写,这类项目没有多人协作、长期迭代的压力,开发效率优先,怎么舒服怎么来
- 准备长期迭代、团队协作、要对外部署生产服务的中大型项目,就别硬坚持这种写法了,后期的维护成本会远大于一开始省下来的“建配对测试文件”的时间成本
如果实在不想来回切文件,也可以用折中方案:把测试代码统一写在业务文件的最末尾,用固定的注释块圈出测试的边界,搭配构建脚本自动剔除注释块里的测试内容,小团队内部用是完全可行的,但一定要提前在项目规范里写清楚测试代码的书写要求,避免出现构建漏过滤、测试逻辑污染业务的问题。
内容的提问来源于stack exchange,提问作者Pompey Magnus
相关产品推荐
相关产品推荐

