JavaScript 中的 Mock 对象和 Fixture:你的测试武器库
\n\n想象一下,你正在编写一个向外部天气 API 发送请求的模块。你运行测试——但它们失败了,因为 API 暂时不可用。或者更糟:你在测试支付系统,每次运行都会从你的卡上扣除真钱。熟悉吗?欢迎来到这样一个世界,在这里 JavaScript 中的 mock 对象和 fixture 成为你最好的朋友。
\n\n在这篇文章中,我们不仅仅列出定义。我们将分析如何利用 mock、stub 和 fixture 使测试变得快速、可靠且独立于外部世界。你将学习如何欺骗定时器、替换 HTTP 请求以及准备完美的测试数据。我们开始吧!
\n\n为什么需要 mock 和 fixture?痛苦与救赎
\n\n当你编写 JavaScript 单元测试 时,你只想测试一个模块。但实际代码通常依赖于数据库、文件系统、网络调用或随机数。没有 mock,每个测试都像一场赌博:它可能因为网络超时而失败,也可能因为服务器返回了错误的数据而失败。
\n\nMock 对象 是真实依赖项的替代品。它们模拟原始对象的行为,但你完全控制它们的响应。Fixture 是预先准备好的数据集(例如,API 响应的 JSON),你可以将它们插入到测试中。它们结合在一起赋予你超能力:你测试的是逻辑,而不是基础设施。
\n\nMock vs Stub vs Spy:理清术语
\n\n在编写代码之前,让我们先理清术语。在 JavaScript 生态系统中(尤其是在 Jest 和 Mocha 中),这些概念有时会被混淆,但区别很重要:
\n\n- \n
- Mock(模拟对象) — 一个替换真实依赖项并允许你设置预期行为的对象。你说:“当调用 id=5 的 getUser 方法时,返回 {name: 'Alice'} 对象。” \n
- Stub(桩) — mock 的简化版本。它只是返回固定的数据,而不检查它是如何被调用的。通常用于替换返回随机值的函数。 \n
- Spy(间谍) — 真实函数的一个包装器。它不改变函数的行为,但会记录它被调用了多少次、使用了哪些参数以及返回了什么。非常适合检查副作用。 \n
在大多数现代框架(Jest, Sinon.js)中,mock 可以做到一切:替换实现和进行间谍活动。但理解其中的区别将有助于你编写更有意义的测试。
\n\nFixture:如何准备测试数据
\n\nJavaScript 测试中的 Fixture 只是你提取到单独文件或变量中的数据。为什么需要这样做?首先,为了避免在每个测试中重复 50 行的 JSON 对象。其次,为了快速更改场景:一个 fixture 用于成功响应,另一个用于错误响应。
\n\n一个用于测试用户管理模块的简单 fixture 示例:
\n\n// fixtures/users.js\nexport const userAlice = {\n id: 1,\n name: 'Alice',\n email: 'alice@example.com',\n role: 'admin'\n};\n\nexport const userBob = {\n id: 2,\n name: 'Bob',\n email: 'bob@example.com',\n role: 'user'\n};\n\nexport const apiResponseSuccess = {\n status: 200,\n data: { users: [userAlice, userBob] }\n};\n\nexport const apiResponseError = {\n status: 500,\n message: 'Internal Server Error'\n};\n\n\n现在在测试中,我们只需导入这些对象。这使代码更简洁,并允许我们轻松重现各种情况:“如果服务器返回 500 会怎样?”
\n\nJest 中的 Mock:实践示例
\n\nJest 是 JavaScript 最流行的测试框架。它内置于 Create React App、Next.js 和许多其他工具中。让我们看看如何在 Jest 中创建 mock 对象。
\n\n使用 jest.fn() 模拟函数
\n\n假设我们有一个函数,它在处理数据后调用一个回调函数。