如何对 vue3 & Pinia 组件进行单元测试

Pinia 是 Vue 3 领域无可争议的状态管理冠军。

它提供了更强大和可扩展的架构,并具有遵循组合 API 语法的优雅代码风格。

入门很简单,特别是如果您是Vue2 + Vuex 开发者,但单元测试中的一些细微差异可能会让您措手不及。

让我们探索一些可能在未来节省您时间的现实生活场景。

为 Pinia Store 增加单元测试

在我们深入进行组件测试之前,让我们先探讨一下如何单独测试一个简单的存储。

为此我们使用 vue cli 创建简单的模板工程,用 计数器 store 做一个演示。

代码如下所示:

bash 复制代码
import { ref, computed } from 'vue'

import { defineStore } from 'pinia'

  


export const useCounterStore = defineStore('counter', () => {

  const count = ref(0)

  


  const doubleCount = computed(() => count.value * 2)

  


  function increment(amount = 1) {

    count.value += amount

  }

  


  return { count, doubleCount, increment }

})

如果你是第一次使用 Pinia , 那以下几个建议会对你有帮助:

  • Vue 中的 方法, 将变为 store 里的 actions

  • Vue 中的 ref 变量 和 reactive 变量 等同于 store 中的 state

  • Vue 中的 computed 属性将等同于 store 中的 getters

增加单元测试很简单:

我们只需要在store 下面 增加目录 test

并创建 counter.spec.js 单元测试文件。

注意:

我们这里使用的单元测试框架是 vittest . 这是一个 vite支持测试运行的框架,具有和jest 完全一样的API,并且运行效率更高一些。

测试代码如下:

bash 复制代码
import { setActivePinia, createPinia } from 'pinia'

import { useCounterStore } from '../counter'

import { beforeEach, describe, it, expect } from 'vitest'

  


describe('Counter Store', () => {

  


    beforeEach(() => {

        setActivePinia(createPinia())

    })

  


    it('increment with no parameters should add one to the counter', () => {

        const counter = useCounterStore()

        expect(counter.count).toBe(0)

        counter.increment()

        expect(counter.count).toBe(1)

    })

  


    it('doubleCount getter should be double the counter at all times', () => {

    const counter = useCounterStore()

        expect(counter.doubleCount).toBe(0)

        counter.increment()

        expect(counter.count).toBe(1)

        expect(counter.doubleCount).toBe(2)

    })

})

在 beforeEach 钩子中,创建并激活了一个 pinia 实例。

没有它,Store 就无法工作,如果我们省略它,将会抛出以下错误:

对 Vue 组件进行单元测试

我们用计数器Store 简单实现一个,计数器的组件。 代码如下:

jsx 复制代码
<script setup>

import { useCounterStore } from '../stores/counter'

const store = useCounterStore()

</script>

  


<template>

    <div class="counterWrap">

        <h1>

            计数器:

        </h1>

        <p class="count">

            当前值:<span> {{  store.count }}</span>

        </p>

        <p class="double-count">

            2倍值: <span> {{ store.doubleCount }} </span>

        </p>

        <button @click="store.increment">

            递增加一

        </button>

    </div>

</template>

因为该组件功能和代码都比较简单,所以实现对该组件的单元测试也比较简单。

jsx 复制代码
import { mount } from "@vue/test-utils";

import Counter from '../Counter.vue'

import { setActivePinia, createPinia } from "pinia";

import { beforeEach, describe, test, expect } from "vitest";

  


describe('Counter Component', () => {

    beforeEach(() => {

        setActivePinia(createPinia())

    })

  


    test('计数器初始值应该为 0', async () => {

        const wrapper = mount(Counter)

        expect(wrapper.find('.count span').text()).toBe('0')

    })

  


    test('按钮点击,计数器的值应该为 1', async () => {

        const wrapper = mount(Counter)

        await wrapper.find('button').trigger('click')

        expect(wrapper.find('.count span').text()).toBe('1')

    })

  


    test('2倍值显示的数应该为当前值 乘 2', async () => {

        const wrapper = mount(Counter)

        expect(wrapper.find('.count span').text()).toBe('0')

        expect(wrapper.find('.double-count span').text()).toBe('0')

        await wrapper.find('button').trigger('click')

        expect(wrapper.find('.count span').text()).toBe('1')

        expect(wrapper.find('.double-count span').text()).toBe('2')

    })

})

值得注意的是, 我们在beforeEach 生命周期中, 再次的创建并激活了 Pinia 实例。

至此,我们的Vue3 组件测试就基本跑通了, 但是存在一个不太明显小问题。

我们正在使用实际的 useCounterStore 实现,并且我们不会单独测试 Counter 组件。

要解决这个问题也不难。 我们需要 安装@pinia/testing 包(npm i -D @pinia/testing)。 这个包提供了 Moc 功能,让我们可以独立测试组件的功能。

首先我们需要安装 npm 包, 并在我们需要在挂载组件时将其作为插件安装。

jsx 复制代码
import { mount } from "@vue/test-utils";

import Counter from '../Counter.vue'

import { describe, test, expect, vi } from "vitest";

import { createTestingPinia } from '@pinia/testing'

  


describe('Counter Mock 测试', () => {

    test('the count initially should be 0', async () => {

        const wrapper = mount(Counter, {

          global: {

            plugins: [

              createTestingPinia({

                createSpy: vi.fn,

              })

            ]

          }

        })

        expect(wrapper.find('.count span').text()).toBe('0')

      })

})

当然,我们也可以通过 initialState 属性,来配置 mock Store 中的初始数据。

示例代码如下:

jsx 复制代码
import { mount } from "@vue/test-utils";

import Counter from '../Counter.vue'

import { describe, test, expect, vi } from "vitest";

import { createTestingPinia } from '@pinia/testing'

  


describe('Counter Mock 测试', () => {

    test('the count initially should be 0', async () => {

        const wrapper = mount(Counter, {

          global: {

            plugins: [

              createTestingPinia({

                createSpy: vi.fn,

                initialState: {

                  counter: {

                    count: 20

                  }

                }

              })

            ]

          }

        })

        expect(wrapper.find('.count span').text()).toBe('20')

      })

})

默认情况下,当我们使用了 createTestingPinia 之后,所有 Store 中的 action 都会被模拟并且不会执行真实Store 中的代码。 我们需要在 createTestingPinia 的方法中,进行模拟action 函数实现。

总结

我们在以上代码中以计数器为例,演示了,如何用vitest 对Vue3 组件和 Pinia Store 进行单元测试。并演示,如何通过 createTestingPinia 对Vue3 组件进行单独的组件逻辑测试而不和Store 进行耦合。

但是,实际开发场景中单元测试的复杂度要远大于目前简单的计数器组件。 我会在未来几天更新一下,牵扯到异步请求的代码如何进行单元测试。 敬请期待吧。

相关推荐
龙猫蓝图2 小时前
vue el-date-picker 日期选择器禁用失效问题
前端·javascript·vue.js
peachSoda72 小时前
随手记:简单实现纯前端文件导出(XLSX)
前端·javascript·vue.js
Tttian6222 小时前
Vue全栈开发旅游网项目(11)-用户管理前端接口联调
前端·vue.js·django
龙猫蓝图4 小时前
vue el-date-picker 日期选择 回显后成功后无法改变的解决办法
前端·javascript·vue.js
刘志辉5 小时前
Pure Adminrelease(水滴框架配置)
vue.js
工业互联网专业5 小时前
Python毕业设计选题:基于Django+uniapp的公司订餐系统小程序
vue.js·python·小程序·django·uni-app·源码·课程设计
黄景圣5 小时前
CURD低代码程序设计
前端·vue.js·后端
lin-lins5 小时前
Vue 模板编译原理
前端·javascript·vue.js
customer086 小时前
【开源免费】基于SpringBoot+Vue.JS课程答疑系统(JAVA毕业设计)
java·jvm·vue.js·spring boot·spring cloud·kafka·开源
J总裁的小芒果6 小时前
el-table中增加校验方法(二)
javascript·vue.js·elementui