能直接替换,前提是请求调用不写死在业务逻辑里——所有 fetch/axios/apiclient 调用需通过参数传入、工厂函数构造或容器注入,测试时用 fake 无缝覆盖,无需修改源码。

能直接替换,前提是请求调用不写死在业务逻辑里——只要所有 fetch/axios/apiClient 调用都通过参数传入、工厂函数构造或容器注入,测试时就能用 Fake 实现无缝覆盖,无需改一行源码。
把请求函数当参数传给 Hook 或 Service
硬编码 fetch 或 axios.get 会导致测试时无法拦截。改成由外层决定具体实现,是最轻量、最可控的解耦方式。
- 错误写法:
useEffect(() => { fetch('/api/user') })—— 全局fetch无法被 Jest 或测试框架接管 - 正确写法:
const useUser = (requestFn = fetch) => { useEffect(() => { requestFn('/api/user') }) } - 生产环境用:
useUser()(默认走真实网络) - 测试环境用:
useUser(mockFetch)(mockFetch可返回预设 JSON 或抛错) - 同理适用于
axios.get、apiClient.getUser等任何请求入口
用工厂函数统一创建 API 客户端实例
散落在各处的 new ApiClient() 是耦合高发区。一旦要切 Mock、加日志、换 base URL,就得全局搜索替换 —— 这不是解耦,是批量字符串替换。
- 新建
services.ts,只导出工厂:export const createApiClient = (options: { baseUrl: string }) => new ApiClient({ baseUrl: options.baseUrl }) - 在应用顶层(如
main.ts或根组件setup)一次性构造:const services = { api: createApiClient({ baseUrl: import.meta.env.VITE_API_URL }) } - 所有业务代码只从
services.api取实例,不自己new - 测试时只需让
createApiClient返回mockApiClient,其余代码完全不动
FastAPI / Spring / napi-rs 等后端场景:用依赖覆盖机制
这类框架原生支持运行时覆盖依赖,比前端手写工厂更声明式、更安全。
- FastAPI:
app.dependency_overrides[get_db] = override_get_db—— 测试中直接替换整个依赖函数 - Spring Boot:
@MockBean UserRepository userRepository—— 自动替换 ApplicationContext 中的 Bean - napi-rs(Rust):
process_data(&mut mock_file_service, ...)—— 函数签名显式接收&dyn FileService,编译期确保可替换 - 关键点:覆盖只在测试生命周期内生效,不影响生产行为;且覆盖目标必须是被框架管理的依赖项,不能是局部变量或闭包内捕获的实例
Vue provide/inject 不是 DI 容器,别滥用
provide('api', new ApiClient()) 看似解耦,实则埋雷:实例在 provide 时就已创建,无法延迟初始化,也无法跨模块复用同一实例。
- ✅ 正确用法:
provide('services', services)—— 提供的是已构造好的稳定对象,类型安全、生命周期可控 - ❌ 错误用法:
provide('api', new ApiClient())—— 每次provide都新建,且无法做依赖校验 - inject 侧也应避免直接解构
inject('api'),而应通过inject('services').api访问,保持层级一致 - 如果要用 inject 做类型推导,建议配合 TypeScript 的
InjectionKey,而不是字符串 key
最容易被忽略的一点:依赖注入本身不解决“如何写 Mock”,它只解决“在哪里换”。Mock 的质量取决于你是否准确模拟了真实依赖的行为边界 —— 比如网络超时、401 响应、空数组返回等,这些必须在 Fake 实现里显式覆盖,否则测试只是看起来过了。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!










