跳转到内容
主菜单
主菜单
移至侧栏
隐藏
导航
首页
最近更改
随机页面
MediaWiki帮助
代码酷
搜索
搜索
中文(中国大陆)
外观
创建账号
登录
个人工具
创建账号
登录
未登录编辑者的页面
了解详情
贡献
讨论
编辑“︁
JavaScript Mock测试
”︁
页面
讨论
大陆简体
阅读
编辑
编辑源代码
查看历史
工具
工具
移至侧栏
隐藏
操作
阅读
编辑
编辑源代码
查看历史
常规
链入页面
相关更改
特殊页面
页面信息
外观
移至侧栏
隐藏
您的更改会在有权核准的用户核准后向读者展示。
警告:
您没有登录。如果您进行任何编辑,您的IP地址会公开展示。如果您
登录
或
创建账号
,您的编辑会以您的用户名署名,此外还有其他益处。
反垃圾检查。
不要
加入这个!
{{DISPLAYTITLE:JavaScript Mock测试}} '''JavaScript Mock测试'''是一种在单元测试中模拟依赖对象行为的技术,用于隔离被测代码与外部系统(如API、数据库或第三方服务)的交互。通过创建模拟对象(Mock Objects),开发者可以控制测试环境,验证代码逻辑的正确性而无需依赖真实的外部资源。 == 核心概念 == === 什么是Mock对象? === Mock对象是真实对象的仿制品,具有以下特征: * 模拟真实对象的行为和接口 * 记录方法调用信息(调用次数、参数等) * 可预定义返回值或抛出异常 * 不包含实际业务逻辑 === 为什么需要Mock测试? === 在测试驱动开发(TDD)中,Mock测试解决了以下问题: * '''隔离性''':避免测试受外部系统不稳定性的影响 * '''确定性''':提供可控的测试环境 * '''速度''':比真实依赖执行更快 * '''验证''':可断言交互行为是否符合预期 == 主要实现方式 == === 手动Mock === 最简单的实现方式是手动创建模拟对象: <syntaxhighlight lang="javascript"> // 真实服务 class PaymentService { process(amount) { // 实际支付逻辑 } } // 手动Mock const mockPaymentService = { calls: [], process(amount) { this.calls.push(amount); return amount < 1000; // 模拟业务规则 } }; // 测试用例 function testSmallPayment() { mockPaymentService.calls = []; // 重置调用记录 const result = mockPaymentService.process(999); console.assert(result === true, "小额支付应成功"); console.assert(mockPaymentService.calls.length === 1, "应调用1次"); } </syntaxhighlight> === 使用Mocking库 === 主流JavaScript测试框架通常提供Mock功能: {| class="wikitable" |+ 常见Mock库对比 ! 库名称 !! 特点 !! 典型用法 |- | Jest || 内置Mock系统 || <code>jest.fn()</code> |- | Sinon.JS || 独立Mock库 || <code>sinon.stub()</code> |- | testdouble || 严格Mock || <code>td.function()</code> |} == Jest Mock示例 == === 基本用法 === <syntaxhighlight lang="javascript"> // 模拟函数 const mockFn = jest.fn(); // 设置返回值 mockFn.mockReturnValue(42); console.log(mockFn()); // 输出: 42 // 验证调用 console.assert(mockFn.mock.calls.length === 1); </syntaxhighlight> === 模块Mock === <syntaxhighlight lang="javascript"> // api.js export const fetchData = () => { /* 真实实现 */ }; // test.js jest.mock('./api'); // 自动mock整个模块 import { fetchData } from './api'; fetchData.mockResolvedValue({ data: 'test' }); test('模拟API调用', async () => { const result = await fetchData(); expect(result).toEqual({ data: 'test' }); }); </syntaxhighlight> == 高级模式 == === Mock实现 === 可以提供自定义实现替代简单返回值: <syntaxhighlight lang="javascript"> const mockFn = jest.fn((a, b) => a + b); console.log(mockFn(2, 3)); // 输出: 5 </syntaxhighlight> === 时序控制 === 模拟异步行为: <syntaxhighlight lang="javascript"> // 模拟延迟响应 mockFn.mockImplementation(() => { return new Promise(resolve => { setTimeout(() => resolve('done'), 1000); }); }); </syntaxhighlight> === 交互验证 === <mermaid> sequenceDiagram participant T as 测试用例 participant M as Mock对象 T->>M: 调用方法(param) M-->>T: 返回预设值 T->>M: 验证调用参数 M-->>T: 返回验证结果 </mermaid> == 实际应用案例 == === 场景:用户注册流程 === 需要测试用户注册逻辑而不实际发送邮件: <syntaxhighlight lang="javascript"> // 真实邮件服务 class EmailService { sendWelcomeEmail(user) { /* 实际实现 */ } } // 测试用例 test('注册后应发送欢迎邮件', () => { // 创建Mock const mockEmailService = { sentEmails: [], sendWelcomeEmail(user) { this.sentEmails.push(user); } }; // 注入Mock const userManager = new UserManager(mockEmailService); userManager.register('test@example.com'); // 验证交互 expect(mockEmailService.sentEmails).toContainEqual( expect.objectContaining({ email: 'test@example.com' }) ); }); </syntaxhighlight> == 最佳实践 == * '''适度Mock''':只Mock必要的依赖,过度Mock会降低测试价值 * '''保持简单''':Mock逻辑应尽可能简单 * '''验证行为''':不仅要验证返回值,还要验证交互过程 * '''清理状态''':测试间重置Mock状态避免污染 * '''文档化''':记录Mock的预期行为 == 数学表达 == Mock测试的覆盖率可以表示为: <math> \text{Mock覆盖率} = \frac{\text{被Mock的依赖数}}{\text{总外部依赖数}} \times 100\% </math> 理想情况下,单元测试应达到: <math> \text{Mock覆盖率} \approx 100\% - \text{集成测试比例} </math> == 常见问题 == === 何时使用Mock vs Stub vs Spy? === * '''Mock''':验证对象间的交互 * '''Stub''':仅替换部分功能 * '''Spy''':包装真实对象并记录调用 === Mock测试的局限性 === * 可能掩盖集成问题 * 维护成本随系统复杂度增加 * 过度使用会导致测试与现实脱节 == 总结 == JavaScript Mock测试是现代化测试策略的重要组成部分,通过模拟依赖关系使单元测试更加聚焦、快速和可靠。掌握Mock技术能显著提升测试代码的质量和维护性,是每位JavaScript开发者应该具备的核心技能。 [[Category:编程语言]] [[Category:JavaScript]] [[Category:Javascript测试]]
摘要:
请注意,所有对代码酷的贡献均被视为依照知识共享署名-非商业性使用-相同方式共享发表(详情请见
代码酷:著作权
)。如果您不希望您的文字作品被随意编辑和分发传播,请不要在此提交。
您同时也向我们承诺,您提交的内容为您自己所创作,或是复制自公共领域或类似自由来源。
未经许可,请勿提交受著作权保护的作品!
取消
编辑帮助
(在新窗口中打开)