如何对 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 进行耦合。

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

相关推荐
一晌小贪欢4 小时前
Python 测试利器:使用 pytest 高效编写和管理单元测试
python·单元测试·pytest·python3·python测试
有来技术5 小时前
Spring Boot 4 + Vue3 企业级多租户 SaaS:从共享 Schema 架构到商业化套餐设计
java·vue.js·spring boot·后端
东东5166 小时前
学院个人信息管理系统 (springboot+vue)
vue.js·spring boot·后端·个人开发·毕设
m0_748229997 小时前
Vue2 vs Vue3:核心差异全解析
前端·javascript·vue.js
汽车仪器仪表相关领域7 小时前
MTX-A 模拟废气温度(EGT)计 核心特性与车载实操指南
网络·人工智能·功能测试·单元测试·汽车·可用性测试
德育处主任Pro9 小时前
『NAS』在群晖部署一款太空策略游戏-ogame-vue-ts
前端·vue.js·游戏
css趣多多11 小时前
render函数
前端·javascript·vue.js
web打印社区11 小时前
前端开发实现PDF打印需求:从基础方案到专业解决方案
前端·vue.js·react.js·electron·pdf
Trae1ounG11 小时前
Vue Iframe
前端·javascript·vue.js
爱上妖精的尾巴12 小时前
8-1 WPS JS宏 String.raw等关于字符串的3种引用方式
前端·javascript·vue.js·wps·js宏·jsa