0Pricing
Vue Academy · 课时

模拟组合式函数、存储与 API

vi.mock()、模拟 useFetch、模拟 Pinia 存储,以及在测试中模拟路由器

模拟组合式函数、存储与 API 是 CoddyKit 上的免费 Vue Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Vue Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Vue Academy 课程共包含 4 节课。

为何使用 mock?

单元测试应当隔离正在测试的组件。真实的网络请求、真实的存储库和复杂的组合式函数会让测试变慢且不稳定。模拟会用可控的伪对象替换这些依赖项。

vi.fn:模拟函数

vi.fn()会创建一个可跟踪的函数。您可以检查它的调用方式,并控制它返回的内容。它是所有模拟操作的基础构件。

import { vi } from "vitest"

const onSave = vi.fn()
onSave("draft")
expect(onSave).toHaveBeenCalledWith("draft")
expect(onSave).toHaveBeenCalledTimes(1)

设置返回值

对于同步代码,可以使用mockReturnValue;对于异步代码,可以使用mockResolvedValue(返回一个已解决的 promise),从而控制模拟对象的输出。

const getId = vi.fn().mockReturnValue(42)
expect(getId()).toBe(42)

const fetchUser = vi.fn().mockResolvedValue({ name: "Ada" })
// await fetchUser() => { name: "Ada" }

模拟组合式函数模块

vi.mock(path)会替换整个模块。请提供一个返回伪导出的工厂函数。您可以用它替换组合式函数,这样组件就能获得可预测的数据。

import { vi } from "vitest"

vi.mock("@/composables/useUser", () => ({
  useUser: () => ({
    user: { value: { name: "Ada" } },
    isLoading: { value: false },
  }),
}))

vi.mock 提升

vi.mock调用会被提升到文件顶部,在导入执行前运行。因此,工厂函数不能引用之后才定义的外部变量——请在工厂函数内部或通过vi.hoisted声明所需的辅助项。

// This runs before imports regardless of position.
vi.mock("@/api", () => ({ getPosts: vi.fn() }))

import { getPosts } from "@/api" // already the mock

测试 Pinia 存储库

使用 Pinia 存储库的组件需要在测试中提供一个存储库。@pinia/testing提供了createTestingPinia,它会为您创建一个操作默认被模拟的存储库。

// terminal: npm install -D @pinia/testing
import { createTestingPinia } from "@pinia/testing"

安装测试 Pinia

挂载组件时,请通过全局plugins选项传入测试 Pinia。您还可以预先设置初始状态。

import { mount } from "@vue/test-utils"
import { createTestingPinia } from "@pinia/testing"

const wrapper = mount(Profile, {
  global: {
    plugins: [createTestingPinia({
      initialState: { user: { name: "Ada" } },
    })],
  },
})

断言存储库操作

默认情况下,操作会被替换为空实现(不会运行),因此您可以断言它们是否被分发。获取存储库并检查间谍函数。

import { useUserStore } from "@/stores/user"

const store = useUserStore()
await wrapper.get("button.logout").trigger("click")
expect(store.logout).toHaveBeenCalled()

监视网络请求

您不必替换整个模块,也可以监视全局对象。vi.spyOn(global, "fetch")会包装真实的 fetch,这样您就可以覆盖并检查它。

const spy = vi.spyOn(global, "fetch").mockResolvedValue({
  ok: true,
  json: async () => ({ items: [1, 2, 3] }),
})

完整的网络请求测试

模拟 fetch,触发操作,刷新 promise,然后断言渲染出的数据,以及 fetch 是否使用正确的 URL 被调用。

it("loads items", async () => {
  vi.spyOn(global, "fetch").mockResolvedValue({
    ok: true,
    json: async () => ({ items: ["a", "b"] }),
  })
  const wrapper = mount(List)
  await flushPromises()
  expect(global.fetch).toHaveBeenCalledWith("/api/items")
  expect(wrapper.findAll("li")).toHaveLength(2)
})

在测试之间重置模拟对象

共享的模拟对象会累积调用历史。请在beforeEach中重置它们(或者在配置中启用clearMocks),这样一个测试就不会影响另一个测试。

import { beforeEach, vi } from "vitest"

beforeEach(() => {
  vi.clearAllMocks()
})

快速检查

检查您对模拟的理解。

总结

您学会了如何在测试中模拟依赖项:

  • vi.fn()会创建可跟踪的函数;使用mockReturnValue / mockResolvedValue控制输出。
  • vi.mock(path, factory)会替换模块(例如组合式函数);它会被提升到导入语句之前。
  • @pinia/testing中的createTestingPinia会提供一个操作被替换为空实现的存储库,您可以对这些操作进行断言。
  • vi.spyOn(global, "fetch").mockResolvedValue(...)可以伪造网络请求。
  • 使用vi.clearAllMocks()在测试之间重置模拟对象。

常见问题解答

「模拟组合式函数、存储与 API」课时是免费的吗?

是的 — 「模拟组合式函数、存储与 API」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Vue Academy 课程的其余内容,请升级到 CoddyKit PRO。 Vue Academy 课程共包含 4 节课。

「模拟组合式函数、存储与 API」这节课中我会学到什么?

vi.mock()、模拟 useFetch、模拟 Pinia 存储,以及在测试中模拟路由器 你通过在浏览器中直接运行的动手代码来练习 Vue Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Vue Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Vue Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「模拟组合式函数、存储与 API」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Vue Academy 课中编写并运行代码吗?

能。每节 Vue Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 为 Vue 项目配置 Vitest
  2. 挂载与查询组件
  3. 测试用户交互与事件
  4. 模拟组合式函数、存储与 API
← 返回 Vue Academy