You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在单元测试中Mock Nuxt 3的useAsyncData与useRuntimeConfig

问题描述

我需要为以下Nuxt 3组件编写单元测试:

<script setup lang="ts">
import { Content, fetchOneEntry, isPreviewing } from '@builder.io/sdk-vue'
import { useRoute } from 'vue-router'

const apiKey = useRuntimeConfig().public.builderIOAPIKey as string
const model = 'page'

const route = useRoute()

const { data: content } = await useAsyncData(`builderData-page-${route.path}`, () => fetchOneEntry({
  model,
  apiKey,
  userAttributes: { urlPath: route.path },
}))
</script>

<template>
  <div id="page">
    <div
      v-if="content || isPreviewing()"
      class="w-full flex items-center justify-center"
    >
      <h1 class="typography-display-3 d-md:typography-display-2 d-lg:typography-display-1">
        {{ (content?.data?.title) || 'Unpublished' }}
      </h1>

      <Content
        :model="model"
        :content="content"
        :api-key="apiKey"
      />
    </div>
  </div>
</template>

当前编写的测试用例如下:

it('renders published content when content exists', async () => {
  (fetchOneEntry as any).mockResolvedValue({ data: { title: 'Test Title' } });
  (isPreviewing as any).mockReturnValue(false)

  const wrapper = mount(LandingPage)
  await flushPromises()

  const heading = wrapper.find('h1')
  expect(heading.exists()).toBe(true)
  expect(heading.text()).toBe('Test Title')

  const contentComponent = wrapper.findComponent(Content)
  expect(contentComponent.exists()).toBe(true)
  expect(contentComponent.props()).toEqual({
    model: 'page',
    content: { data: { title: 'Test Title' } },
    apiKey: 'test-api-key',
  })

  expect(fetchOneEntry).toHaveBeenCalledWith({
    model: 'page',
    apiKey: 'test-api-key',
    userAttributes: { urlPath: '/test-path' },
  })
})

但挂载wrapper时无HTML输出,原因是测试环境中useRuntimeConfig和useAsyncData不可用。尝试过通过globalThis、vi.mock及mockNuxtImport来Mock这两个组合式函数,但均未成功。请问正确的Mock方式是什么?


解决方案

核心思路

Nuxt 3的内置组合式函数必须用官方测试工具提供的mockNuxtImport模拟,同时要统一处理路由和Builder.io的依赖,确保异步逻辑在测试中正确触发。

1. 统一模拟所有依赖

在测试文件顶部添加以下模拟代码,覆盖所有需要的组合式函数和第三方依赖:

import { mockNuxtImport, mount } from '@nuxt/test-utils/runtime'
import { flushPromises, ref } from '@vue/test-utils'
import LandingPage from './LandingPage.vue'
import { Content, fetchOneEntry, isPreviewing } from '@builder.io/sdk-vue'

// 模拟useRuntimeConfig,返回预设的API密钥
mockNuxtImport('useRuntimeConfig', () => {
  return () => ({
    public: {
      builderIOAPIKey: 'test-api-key'
    }
  })
})

// 模拟useAsyncData,返回包含ref包裹数据的对象,适配组件的异步逻辑
mockNuxtImport('useAsyncData', () => {
  return (_key: string, handler: () => Promise<any>) => {
    return {
      data: ref(handler())
    }
  }
})

// 模拟useRoute,返回预设的路由路径
mockNuxtImport('useRoute', () => {
  return () => ({
    path: '/test-path'
  })
})

// 整体模拟Builder.io的SDK,避免单独mock的作用域问题
vi.mock('@builder.io/sdk-vue', () => ({
  Content: vi.fn(),
  fetchOneEntry: vi.fn().mockResolvedValue({ data: { title: 'Test Title' } }),
  isPreviewing: vi.fn().mockReturnValue(false)
}))

2. 调整测试用例

保留原有断言逻辑即可,无需额外修改:

it('renders published content when content exists', async () => {
  const wrapper = mount(LandingPage)
  await flushPromises()

  // 验证标题渲染
  const heading = wrapper.find('h1')
  expect(heading.exists()).toBe(true)
  expect(heading.text()).toBe('Test Title')

  // 验证Content组件的props传递
  const contentComponent = wrapper.findComponent(Content)
  expect(contentComponent.exists()).toBe(true)
  expect(contentComponent.props()).toEqual({
    model: 'page',
    content: { data: { title: 'Test Title' } },
    apiKey: 'test-api-key',
  })

  // 验证fetchOneEntry的调用参数
  expect(fetchOneEntry).toHaveBeenCalledWith({
    model: 'page',
    apiKey: 'test-api-key',
    userAttributes: { urlPath: '/test-path' },
  })
})

关键注意事项

  • 必须使用@nuxt/test-utils/runtime提供的mockNuxtImport模拟Nuxt专属组合式函数,这是官方推荐的可靠方式,比直接修改globalThis更适配Nuxt的模块机制。
  • 模拟useAsyncData时,返回的data必须是ref类型,确保组件能响应数据变化,触发模板渲染。
  • 对第三方SDK(如Builder.io)使用vi.mock整体模拟,避免单独mock函数时出现的作用域冲突问题。

内容的提问来源于stack exchange,提问作者Nessario

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 07:16:18