高可测试性的依赖注入代码核心在于显式传入抽象依赖、避免内部创建和全局状态。需通过构造函数或参数注入接口,面向协议编程,确保测试时能零成本替换依赖且不依赖框架。

高可测试性的依赖注入代码,核心不是“用了 DI 框架”,而是让依赖真正可替换、可控制、不污染测试上下文。关键在于设计方式,而非语法糖。
依赖必须通过参数显式传入
禁止在类内部或函数体顶层直接 new 实例、调用全局单例、读取配置文件或连接数据库。所有外部依赖(数据库、HTTP 客户端、随机生成器、日志器等)都应作为构造函数参数、函数参数或依赖函数的输入项。
- C++ 中用接口指针/引用传入,如 Logger(NetworkUploader& uploader)
- Go 中结构体字段声明为接口类型,如 repo UserRepository,并通过 NewUserService(repo) 构造
- Java/Spring 中只用构造器注入,避免 @Autowired 字段,确保无容器也能 new 对象并注入 mock
- FastAPI 中依赖函数不提前实例化 db,而是在函数体内创建或 yield,便于测试时 override
面向接口/协议编程,而非具体实现
业务代码永远只依赖抽象——接口、Protocol、自定义函数类型或 driver.Conn 这类标准接口。这样测试时才能提供一个行为可控的替代实现,且不破坏类型检查。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- Python:用 AsyncSession 或自定义 DatabaseProtocol,而非 SQLAlchemyDatabase
- Go:用 io.Reader/io.Writer 替代 os.Stdin/os.Stdout;用 func() string 替代直接调用 helpers.RandomString
- Spring:定义 UserRepository 接口,让业务类依赖它,而不是 JpaRepository 具体实现
依赖生命周期与测试上下文对齐
避免跨请求/跨测试共享状态。模块级变量、全局连接池、lru_cache 不清理,都会导致测试间干扰。
- FastAPI 中依赖函数默认每次请求新建,就别在函数外层初始化 db = Database()
- Go 的 wire provider 函数里,不要复用 *sql.DB 实例,除非明确加锁或设为单例;测试时用 sqlmock.New() 返回新实例
- Spring 原型 Bean 若含运行时参数(如 Options),必须通过构造器传入,不能靠 @Value 或静态配置,否则测试无法控制输入
测试时能零成本替换,无需框架也能测
可测试性高的代码,应该支持“不启动容器、不引入 mock 框架”就能完成核心逻辑验证。
- Go 中把 stdin/stdout 抽成 io.Reader/io.Writer 参数,测试直接传 strings.NewReader("yes") 和 &bytes.Buffer{}
- FastAPI 测试时用 app.dependency_overrides[get_db] = lambda: mock_db,一行即生效
- C++ 使用 Google Mock 时,MockUploader 继承 NetworkUploader 接口,测试中 new MockUploader() 后传给 Logger 构造器即可










