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

使用Vitest与Vuetify进行快照组件测试:组件解析失败问题

问题:Vue 3 + Vuetify 3 快照测试警告与空快照问题及配置疑问

问题背景

在Vue 3 + Vuetify 3环境下使用Vitest对ConfirmationModal组件执行快照测试时遇到两个核心问题:

  1. 仅初始化createVuetify()时,快照内容正常,但会抛出大量Vuetify组件未解析的警告(如v-layout无法解析)。
  2. 按照官方文档引入所有Vuetify组件和指令后,警告消失,但生成的快照几乎为空,失去测试意义。
  3. 组件库项目(无完整Vue应用)不确定应使用vite.config.ts还是vitest.config.ts进行配置。

相关代码与现象

初始测试代码(有警告但快照正常)

import { createVuetify } from 'vuetify';
import { describe, it, expect } from 'vitest';
import ConfirmationModal from '@/components/ConfirmationModal.vue';

const vuetify = createVuetify();

global.ResizeObserver = require('resize-observer-polyfill');

describe(`Composant ConfirmationModal`, () => {
  it(`Match snapshot`, async () => {
    const wrapper = mount(
      {
        template: '<v-layout><confirmation-modal></confirmation-modal></v-layout>'
      },
      {
        props: {
          value: true,
          title: 'test',
          width: 100
        },
        global: { components: { ConfirmationModal }, plugins: [vuetify] }
      }
    );
    expect(wrapper.html()).toMatchSnapshot();
  });
});

初始快照内容

// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`Composant ConfirmationModal > Match snapshot 1`] = `
"<v-layout title=\"test\" width=\"100\" value=\"true\">
  <div class=\"text-center\">
    <v-dialog width=\"510\" class=\"confirmation-modal\" persistent=\"\" value=\"false\">
      <v-card>
        <v-card-title class=\"text-h5 grey lighten-2\" primary-title=\"\"></v-card-title>
        <v-card-text></v-card-text>
        <v-divider></v-divider>
        <v-card-actions>
          <v-spacer></v-spacer>
          <v-btn class=\"btn-ok\" color=\"primary\" text=\"\"> OK</v-btn>
          <v-btn id=\"btn-annulation\" color=\"primary\" text=\"\"> Annuler</v-btn>
        </v-card-actions>
      </v-card>
    </v-dialog>
  </div>
</v-layout>"
`;

组件未解析警告

[Vue warn]: Failed to resolve component: v-layout
If this is a native custom element, make sure to exclude it from component resolution via compilerOptions.isCustomElement. 
  at <Anonymous value=true title="test" width=100  ... > 
  at <VTUROOT>

引入完整Vuetify组件后的配置(警告消失但快照为空)

import { createVuetify } from 'vuetify'
import * as components from 'vuetify/components'
import * as directives from 'vuetify/directives'

const vuetify = createVuetify({
  components,
  directives,
})

空快照内容

exports[`Composant ConfirmationModal > Correspond au snapshot 1`] = `
"<div class=\"v-layout\" title=\"test\" width=\"100\" value=\"true\">
  <div class=\"text-center\">
    <!---->
    <!---->
  </div>
</div>"
`;

当前vitest.config.ts配置

import { defineConfig, loadEnv } from 'vite';
import { fileURLToPath, URL } from 'node:url';
import vue from '@vitejs/plugin-vue';

export default ({ mode }) => {
  const env = loadEnv(mode, process.cwd(), '');
  return defineConfig({
    plugins: [vue()],
    define: {
      'process.env': env
    },
    test: {
      globals: true,
      environment: 'jsdom',
      deps: {
        inline: ['vuetify']
      },
    },
    resolve: {
      alias: {
        '@': fileURLToPath(new URL('./src', import.meta.url))
      }
    }
  });
};

解决方案

1. 解决警告与空快照的矛盾

修改测试代码

import { createVuetify } from 'vuetify';
import * as components from 'vuetify/components';
import * as directives from 'vuetify/directives';
import { describe, it, expect, vi } from 'vitest';
import { mount } from '@vue/test-utils';
import ConfirmationModal from '@/components/ConfirmationModal.vue';

// 模拟ResizeObserver,避免无关警告
vi.stubGlobal('ResizeObserver', require('resize-observer-polyfill'));

const vuetify = createVuetify({
  components,
  directives,
});

describe(`Composant ConfirmationModal`, () => {
  it(`Match snapshot`, async () => {
    // 直接挂载目标组件,而非废弃的v-layout
    const wrapper = mount(ConfirmationModal, {
      props: {
        // Vue3中v-model的prop名改为modelValue(若组件仍使用value需同步调整)
        modelValue: true,
        title: 'test',
        width: 100
      },
      global: {
        plugins: [vuetify]
      }
    });

    // 等待Vuetify组件异步渲染完成
    await wrapper.vm.$nextTick();

    expect(wrapper.html()).toMatchSnapshot();
  });
});

关键调整说明

  • 移除废弃组件:Vuetify3已删除v-layout组件,直接挂载ConfirmationModal避免无效布局容器干扰。
  • 修正v-model prop:Vue3中v-model的绑定prop从value改为modelValue,确保模态框显示状态生效。
  • 等待DOM更新:Vuetify组件渲染存在异步过程,通过$nextTick()确保快照捕获完整渲染状态。

2. 组件库项目配置选择

  • 通用场景:若构建与测试配置差异不大,直接在vite.config.ts的test字段中配置测试规则即可,Vitest会自动读取该配置。
  • 独立测试需求:若测试需要与构建不同的配置(如特殊alias、插件),可创建vitest.config.ts,导入基础Vite配置并扩展测试专属规则:
    import { defineConfig } from 'vitest/config';
    import viteConfig from './vite.config';
    
    export default defineConfig({
      ...viteConfig,
      test: {
        ...viteConfig.test,
        // 添加测试专属配置
        environment: 'jsdom',
        globals: true,
      },
    });
    

原因分析

  1. 初始配置警告:仅调用createVuetify()未注册组件/指令,Vuetify不会全局注册组件,导致测试时无法解析v-dialog、v-card等组件。
  2. 空快照问题:
    • 使用了Vuetify3废弃的v-layout组件,完整引入Vuetify后该组件无法正常渲染,导致内部ConfirmationModal挂载异常。
    • Vue3 v-model prop名变更未同步,导致模态框显示状态未触发,v-dialog处于隐藏状态(快照中显示为<!---->)。
    • 未等待组件异步渲染完成,直接生成快照捕获了未初始化的DOM状态。

内容的提问来源于stack exchange,提问作者Clément Picou

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 20:11:02