vue3组件测试中如何修改pinia中state的值并影响组件?

使用vue-test-utils来测试使用pinia的组件,需要修改pinia中存储的state的值,但是尝试了很多方法都无济于事。原始组件和存储文件如下。

// HelloWorld.vue
<template>
    <h1>{{ title }}</h1>
</template>

<script>
import { useTestStore } from "@/stores/test";
import { mapState } from "pinia";

export default {
    name: "HelloWorld",

    computed: {
        ...mapState(useTestStore, ["title"]),
    },
};
</script>
// @/stores/test.js
import { defineStore } from "pinia";

export const useTestStore = defineStore("test", {
    state: () => {
        return { title: "hhhhh" };
    },
});

已尝试以下方法。

  1. 将组件内使用的store导入到测试代码中,直接进行修改,但修改不会影响组件。

    
    // test.spec.js
    import { mount } from "@vue/test-utils";
    import { createTestingPinia } from "@pinia/testing";
    import HelloWorld from "@/components/HelloWorld.vue";
    import { useTestStore } from "@/stores/test";

test("pinia in component test", () => {
const wrapper = mount(HelloWorld, {
global: {
plugins: [createTestingPinia()],
},
});
const store = useTestStore();
store.title = "xxxxx";

console.log(wrapper.text()) //"hhhhh";
});


2. 使用initialState 试图覆盖原始存储的内容,但再次没有任何效果。

// test.spec.js
import { mount } from "@vue/test-utils";
import { createTestingPinia } from "@pinia/testing";
import HelloWorld from "@/components/HelloWorld.vue";

test("pinia in component test", () => {
const wrapper = mount(HelloWorld, {
global: {
plugins: [createTestingPinia({ initialState: { title: "xxxxx" } })],
},
});
console.log(wrapper.text()) //"hhhhh";
});


3.修改测试代码中传递给global.plugins的TestingPinia对象,还是没有效果。

// test.spec.js
import { mount } from "@vue/test-utils";
import { createTestingPinia } from "@pinia/testing";
import HelloWorld from "@/components/HelloWorld.vue";

test("pinia in component test", () => {
const pinia = createTestingPinia();
pinia.state.value.title = "xxxxx";
const wrapper = mount(HelloWorld, {
global: {
plugins: [pinia],
},
});
console.log(wrapper.text()) //"hhhhh";
});


4. 使用 global.mocks 来模拟组件中使用的状态,但这仅对组件中用 setup() 传入的状态有效,而用 mapState() 传入的状态无效。

// test.spec.js
import { mount } from "@vue/test-utils";
import { createTestingPinia } from "@pinia/testing";
import HelloWorld from "@/components/HelloWorld.vue";

test("pinia in component test", () => {
const wrapper = mount(HelloWorld, {
global: {
plugins: [createTestingPinia()],
mocks: { title: "xxxxx" },
},
});
console.log(wrapper.text()) //"hhhhh"
});

stack overflow How can I modify the value of state in pinia in vue3 component test and affect the component?
原文答案

答案:

作者头像

这已使用 jest.mock() 解决。

import { mount } from "@vue/test-utils";
import { createPinia } from "pinia";
import HelloWorld from "@/components/HelloWorld.vue";

jest.mock("@/stores/test", () => {
    const { defineStore } = require("pinia");
    const useTestStore = defineStore("test", { state: () => ({ title: "xxxxx" }) });
    return { useTestStore };
});

test("pinia in component test", () => {
    const wrapper = mount(HelloWorld, {
        global: { plugins: [createPinia()] },
    });
    expect(wrapper.text()).toBe("xxxxx");
});
作者头像

感谢 Red Panda 提供这个主题。我使用“testing-library”和“vue-testing-library”而不是“vue-test-utils”和“jest”,但问题是一样的 - 无法更改商店的 pinia 初始数据。我终于找到了解决此问题的方法,而无需模拟该功能。当你 $patch 数据时,你只需要 await 就可以了。不知何故,它有帮助。我的代码看起来像这样,它完全有效:

Popup.test.js

import { render, screen } from '@testing-library/vue'
import { createTestingPinia } from '@pinia/testing'
import { popup } from '@/store1/popup/index'
import Popup from '../../components/Popup/index.vue'

describe('Popup component', () => {
  test('displays popup with group component', async () => {
    render(Popup, {
      global: { plugins: [createTestingPinia()] }
    })
    const store = popup()
    await store.$patch({ popupData: 'new name' })
    screen.debug()
  })
})

或者您可以使用以下方案设置 initialState:

import { render, screen } from '@testing-library/vue'
import { createTestingPinia } from '@pinia/testing'
import { popup } from '@/store1/popup/index'
import Popup from '../../components/Popup/index.vue'

  test('displays popup with no inner component', async () => {
    const { getByTestId } = render(Popup, {
      global: {
        plugins: [
          createTestingPinia({
            initialState: {
              popup: {
                popupData: 'new name'
              }
            }
          })
        ]
      }
    })
    const store = popup()
    screen.debug()
  })

其中 popup in initialState - 是从 @/store1/popup 导入的 pinia 商店。您可以以相同的方式在那里指定其中的任何一个。

弹出窗口

<script>
import { defineAsyncComponent, markRaw } from 'vue'
import { mapState, mapActions } from 'pinia'
import { popup } from '@/store1/popup/index'

export default {
  data () {
    return {}
  },
  computed: {
    ...mapState(popup, ['popupData'])
  },
....

enter image description here

作者头像

我正在使用带有组合 API 样式的 Vue 3 开发一个项目。

组合 API 用于组件和定义我的商店。

这是我的商店

player.js

import { defineStore } from 'pinia'
import { ref, reactive } from 'vue'

export const usePlayerStore = defineStore('player',()=>{
    const isMainBtnGameClicked = ref(false)

    return { isMainBtnGameClicked }
})

MyComponent.vue

//import { usePlayerStore } from '...'
const playerStore = usePlayerStore()        
playerStore.isMainBtnGameClicked = true

我商店中的 isMainBtnGameClicked 已正确更新。

您还可以通过引用 pinia 存储来更新组件中的变量。它在我的项目中工作。

作者头像

为了让我以后省去很多时间的麻烦,这里有一个不明显的东西在起作用——事件循环。 Vue 反应性依赖于运行的事件循环来触发级联状态更改。

当你使用 vue-test-utils mount/shallowMount/render 组件时,不会自动运行事件循环。您必须手动触发它才能触发反应,例如

await component.vm.$nextTick;

如果你不想弄乱滴答声,你必须模拟商店状态/吸气剂/等。 (文档强烈倾向于,但没有解释必要性)。 OP在这里嘲笑了整个商店。

另见: Vue-test-utils: using $nextTick multiple times in a single test