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

如何独立单元测试Pinia Store中的Watcher?

Pinia Store中Watcher单元测试的独立验证方案

问题背景

在测试基于Pinia的布局Store时,需要单独验证监听sidebarMode计算属性的Watcher逻辑——当sidebarMode切换为Static时,自动将sidebarOverlayActive重置为false。由于sidebarMode依赖@vueuse/core的useWindowSize,希望避免依赖window.innerWidth的Mock,单独聚焦Watcher的行为验证。

Store代码如下:

export enum SidebarMode {
  Static = 'static',
  Overlay = 'overlay',
}

export const useLayoutStore = defineStore('layout', () => {
  const { width: windowWith } = useWindowSize();
  const sidebarMode = computed(() => windowWith.value >= 991 ? SidebarMode.Static : SidebarMode.Overlay);

  const sidebarCollapsed = ref(false);
  const sidebarOverlayActive = ref(false);

  function toggleSidebar() {
    switch (sidebarMode.value) {
      case SidebarMode.Static:
        sidebarCollapsed.value = !sidebarCollapsed.value;
        break;
      case SidebarMode.Overlay:
        sidebarOverlayActive.value = !sidebarOverlayActive.value;
        break;
    }
  }

  watch(sidebarMode, (newMode) => {
    if (newMode === SidebarMode.Static)
      sidebarOverlayActive.value = false;
  });

  return { sidebarMode, sidebarCollapsed, sidebarOverlayActive, toggleSidebar };
});

解决方案

方案一:Mock useWindowSize 实现可控的宽度值

通过Mock@vueuse/core的useWindowSize方法,返回一个可手动修改的ref,以此精准控制sidebarMode的变化,从而单独验证Watcher的逻辑。这种方式不依赖真实的window.innerWidth,完全隔离了计算属性的测试场景。

测试代码示例:

import { useLayoutStore } from '@/stores/layout';
import { useWindowSize } from '@vueuse/core';
import { createPinia, setActivePinia } from 'pinia';
import { vi, describe, it, expect, beforeEach, nextTick } from 'vitest';
import { ref } from 'vue';
import { SidebarMode } from '@/stores/layout';

// 全局Mock useWindowSize
vi.mock('@vueuse/core', () => ({
  useWindowSize: vi.fn(),
}));

describe('useLayoutStore - sidebarMode watcher', () => {
  beforeEach(() => {
    // 重置Pinia实例
    setActivePinia(createPinia());
  });

  it('重置sidebarOverlayActive当sidebarMode切换为Static', async () => {
    // 创建可控制的宽度ref,初始值对应Overlay模式
    const mockWidth = ref(768);
    (useWindowSize as vi.Mock).mockReturnValue({ width: mockWidth });

    const store = useLayoutStore();
    // 先将overlay状态设为true
    store.sidebarOverlayActive = true;
    expect(store.sidebarOverlayActive).toBe(true);

    // 修改宽度,触发sidebarMode切换为Static
    mockWidth.value = 1024;
    // 等待Watcher异步执行
    await nextTick();

    expect(store.sidebarOverlayActive).toBe(false);
  });

  it('sidebarMode切换为Overlay时不修改sidebarOverlayActive', async () => {
    const mockWidth = ref(1024); // 初始为Static模式
    (useWindowSize as vi.Mock).mockReturnValue({ width: mockWidth });

    const store = useLayoutStore();
    store.sidebarOverlayActive = true;
    expect(store.sidebarOverlayActive).toBe(true);

    // 修改宽度切换为Overlay模式
    mockWidth.value = 768;
    await nextTick();

    expect(store.sidebarOverlayActive).toBe(true);
  });
});

方案二:直接替换Store中的sidebarMode为可控Ref

如果不想MockuseWindowSize,可以通过重定义Store的sidebarMode属性,用一个可手动修改的ref替换原计算属性,直接触发Watcher的逻辑。

测试代码示例:

import { useLayoutStore } from '@/stores/layout';
import { createPinia, setActivePinia } from 'pinia';
import { vi, describe, it, expect, beforeEach, nextTick } from 'vitest';
import { ref } from 'vue';
import { SidebarMode } from '@/stores/layout';

describe('useLayoutStore - sidebarMode watcher', () => {
  beforeEach(() => {
    setActivePinia(createPinia());
  });

  it('手动切换sidebarMode为Static时触发状态重置', async () => {
    const store = useLayoutStore();
    // 创建可控制的sidebarMode ref
    const mockSidebarMode = ref(SidebarMode.Overlay);
    
    // 替换原计算属性为自定义ref的getter
    Object.defineProperty(store, 'sidebarMode', {
      get: () => mockSidebarMode.value,
      configurable: true
    });

    store.sidebarOverlayActive = true;
    expect(store.sidebarOverlayActive).toBe(true);

    // 手动修改sidebarMode值
    mockSidebarMode.value = SidebarMode.Static;
    await nextTick();

    expect(store.sidebarOverlayActive).toBe(false);
  });
});

方案说明

  • 方案一更符合单元测试的隔离原则,通过Mock依赖实现对Watcher逻辑的精准测试,同时与sidebarMode的计算逻辑测试完全解耦。
  • 方案二更直接,但需要修改Store的属性定义,适合快速验证场景,不推荐作为长期测试方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 11:58:10