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

如何用Vitest Mock方法测试Vue组件的onVisibleChange函数?

Vue组件单元测试:Mock onChange验证onVisibleChange调用失败的解决方法

问题背景

需要Mock组件内的onChange方法,验证onVisibleChange是否正确调用它,但现有测试代码的两种尝试均报错:

  • Spy断言失败,提示onChange未被调用
  • 替换实例方法后,内部调用仍未触发mock函数

组件代码(MyComponent.vue)

<script setup>
import { ref } from "vue";

const isSelectAll = ref(true);
const selectAllModel = ref(false);

const onChange = () => {
    isSelectAll.value = true;
    selectAllModel.value = false;
};
const onVisibleChange = () => {
  // do something than call
  onChange();
};
</script>

原测试代码及报错

import { mount } from 'vitest';
import { ref } from 'vue';
import MyComponent from './MyComponent.vue';

describe('MyComponent', () => {
  const wrapper = shallowMount(MyComponent);
  const spy = vi.spyOn(wrapper.vm, 'onChange');
  wrapper.vm.onVisibleChange();
  expect(spy).toHaveBeenCalled();
  expect(wrapper.vm.onChange).toHaveBeenCalled();
  // 报错:AssertionError: expected "onChange" to be called at least once

  // 另一种尝试
  const mock = vi.fn();
  wrapper.vm.onChange = mock;
  wrapper.vm.onVisibleChange();
  expect(mock).toHaveBeenCalled();   // 报错:AssertionError: expected "spy" to be called at least once
  expect(wrapper.vm.onChange).toHaveBeenCalled();  // 报错:TypeError: [Function onChange] is not a spy or a call to a spy!
})

失败原因

<script setup>是闭包模式,内部定义的onChange是私有变量,不会挂载到组件实例wrapper.vm上。即使后续通过某种方式暴露,onVisibleChange内部调用的始终是闭包中的原始函数引用,外部替换实例上的方法不会影响内部的调用逻辑。

解决方法

方法1:用ref包裹函数实现可Mock的引用

修改组件代码,将onChange用ref包裹,让函数引用可被外部修改:

<script setup>
import { ref } from "vue";

const isSelectAll = ref(true);
const selectAllModel = ref(false);

// 用ref包裹函数,使引用可修改
const onChange = ref(() => {
    isSelectAll.value = true;
    selectAllModel.value = false;
});

const onVisibleChange = () => {
  // 调用ref的value属性
  onChange.value();
};

// 暴露方法到组件实例
defineExpose({
  onChange,
  onVisibleChange
});
</script>

对应的测试代码:

import { shallowMount } from 'vitest';
import MyComponent from './MyComponent.vue';

describe('MyComponent', () => {
  it('onVisibleChange should trigger onChange', () => {
    const wrapper = shallowMount(MyComponent);
    // 替换ref的value为mock函数
    const mockOnChange = vi.fn();
    wrapper.vm.onChange.value = mockOnChange;

    wrapper.vm.onVisibleChange();

    expect(mockOnChange).toHaveBeenCalled();
  });
});

方法2:提取逻辑到独立模块,Mock模块导出

将onChange的核心逻辑提取到单独的工具模块,测试时Mock该模块的导出:

  1. 创建utils.js:
export const handleOnChange = (isSelectAll, selectAllModel) => {
  isSelectAll.value = true;
  selectAllModel.value = false;
};
  1. 修改组件代码:
<script setup>
import { ref } from "vue";
import { handleOnChange } from './utils.js';

const isSelectAll = ref(true);
const selectAllModel = ref(false);

const onChange = () => {
    handleOnChange(isSelectAll, selectAllModel);
};

const onVisibleChange = () => {
  onChange();
};
</script>
  1. 测试代码:
import { shallowMount } from 'vitest';
import MyComponent from './MyComponent.vue';
import * as utils from './utils.js';

describe('MyComponent', () => {
  it('onVisibleChange should call onChange logic', () => {
    // Mock工具函数
    const spy = vi.spyOn(utils, 'handleOnChange').mockImplementation(() => {});

    const wrapper = shallowMount(MyComponent);
    wrapper.vm.onVisibleChange();

    expect(spy).toHaveBeenCalled();
    // 恢复原始函数
    spy.mockRestore();
  });
});

方法3:重构组件逻辑,通过props传递函数(适合复杂场景)

如果组件逻辑更复杂,可以将onChange设计为外部传入的props,测试时直接传入mock函数即可:

组件代码示例:

<script setup>
const props = defineProps({
  onChange: {
    type: Function,
    default: () => {
      // 默认逻辑
      isSelectAll.value = true;
      selectAllModel.value = false;
    }
  }
});

const isSelectAll = ref(true);
const selectAllModel = ref(false);

const onVisibleChange = () => {
  props.onChange();
};
</script>

测试代码:

import { shallowMount } from 'vitest';
import MyComponent from './MyComponent.vue';

describe('MyComponent', () => {
  it('onVisibleChange should call passed onChange', () => {
    const mockOnChange = vi.fn();
    const wrapper = shallowMount(MyComponent, {
      props: {
        onChange: mockOnChange
      }
    });

    wrapper.vm.onVisibleChange();

    expect(mockOnChange).toHaveBeenCalled();
  });
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 13:53:22