Vue3项目如何利用vitest做单元测试

lxf2023-03-12 09:33:01

为了提升代码质量,降低 bug 率,开始在前端项目中增加单元测试,重点是对组件的单元测试,避免因为修改公共组件导致的隐匿性 bug。考虑到我们的项目主要是用 vue 框架,因此优先采用 vitest+@vue/test-utils 来实现

初始化一个 vue3 项目

pnpm create vite vue-vitest --template vue-ts
cd vue-test
pnpm install
pnpm run dev

接入 vitest

安装 vitest

# happy-dom用来模拟DOM和浏览器API
# @vitest/coverage-istanbul 用来设置测试覆盖率报告的提供者,使用c8目前不能忽略代码
pnpm add vitest @vue/test-utils happy-dom @vitest/coverage-istanbul -D

vitest 配置

因为新建的项目是 vue+vite 项目,故可以在 vite.config.ts 中直接配置

如果要配置 vitest 本身,请在你的 Vite 配置中添加 test 属性。 你还需要使用 三斜线命令 ,同时如果是从 vite 本身导入 defineConfig,请在配置文件的顶部加上三斜线命令。

/// <reference types="vitest" />

import vue from "@vitejs/plugin-vue";
import { defineConfig } from "vite";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  test: {
    // 模拟dom环境
    environment: "happy-dom",
    coverage: {
      // 覆盖率提供者
      provider: "istanbul",
      reporter: ["text", "json", "html"],
      // 设置覆盖文件夹
      reportsDirectory: "./coverage",
      // 检查每个文件的阈值
      perFile: true,
      // 设置代码覆盖率阈值
      lines: 75,
      functions: 75,
      branches: 75,
      statements: 75,
    },
    open: true,
    include: ["./src/components/**/*.{test,spec}.ts"],
  },
});

package.json 文件中增加命令

{
  // ...
  "scripts": {
    // ...
    "test": "vitest",
    "coverage": "vitest run --coverage"
  }
}

在 components 文件夹中增加组件

  1. components/Hello/index.vue
<template>
  <div>{{ count }} x {{ times }} = {{ result }}</div>
  <button @click="times += 1">x1</button>
</template>

<script setup lang="ts">
import { computed, ref } from "vue";

const props = defineProps<{ count: number }>();

const times = ref(2);
const result = computed(() => props.count * times.value);

defineExpose(props);
</script>
  1. components/World/index.vue
<template>
  <div>
    <div class="message">
      {{ message }}
    </div>
    Enter your username: <input :value="name" />
    <div v-if="error" class="error">
      Please enter a username with at least seven letters.
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed } from "vue";

const props = withDefaults(
  defineProps<{
    message: string;
    username: string;
  }>(),
  {
    message: "",
    username: "",
  }
);

const name = computed(() => props.username);

const error = computed(() => {
  return props.username.trim().length < 7;
});
</script>

编写测试用例

// Hello/__test__/index.spec.ts
import { mount } from "@vue/test-utils";
import { describe, expect, test } from "vitest";

import Hello from "../index.vue";
describe("Hello", () => {
  test("挂载组件", async () => {
    expect(Hello).toBeTruthy();

    const wrapper = mount(Hello, {
      props: {
        count: 4,
      },
    });

    await wrapper.get("button").trigger("click");

    expect(wrapper.text()).toContain("4 x 3 = 12");

    await wrapper.get("button").trigger("click");

    expect(wrapper.text()).toContain("4 x 4 = 16");
  });
});
// World/__test__/index.spec.ts
import { shallowMount } from "@vue/test-utils";
import { describe, expect, test } from "vitest";

import World from "../index.vue";

describe("World", () => {
  test("renders a message and responds correctly to user input", async () => {
    const wrapper = shallowMount(World, {
      props: {
        message: "Hello World",
        username: "",
      },
    });

    // 确认是否渲染了 `message`
    expect(wrapper.find(".message").text()).toEqual("Hello World");

    // 断言渲染了错误信息
    expect(wrapper.find(".error").exists()).toBeTruthy();

    // 更新 `username` 并断言错误信息不再被渲染
    await wrapper.setProps({ username: "Lachlan" });
    expect(wrapper.find(".error").exists()).toBeFalsy();
  });
});

具体测试用例编写可以参考如何编写vue3组件测试用例?

调试测试用例

在 VSCode 中调试测试的快速方法是通过 Javascript Debug Terminal。打开一个新的 JavaScript 调试终端,直接运行 npm run test 或 vitest。 Vue3项目如何利用vitest做单元测试

利用 husky 和 lint-staged 达到自动化测试

安装并启用 husky

yarn add husky -D
npx husky install

在 scripts 中加入命令

{
  "scripts": {
    "prepare": "husky install"
  }
}

创建一个 pre-commit 的 hooks 文件

npx husky add .husky/pre-commit "npx --no-install lint-staged"

这样在 git commit 命令执行前会执行 lint-staged 中配置的命令

安装并启用 lint-stated

yarn  add lint-stated

在 package.json 中加入配置

{
  "lint-staged": {
    "*.{vue,tsx}": ["vitest related --run"]
  }
}

这样在提交 vue 文件的时候就能自动执行该文件的测试用例,测试用例执行通过后方可提交

如果想要设置代码覆盖率达到一定数值方可提交,可以在 package.json 中加入命令参数 coverage,同时在 vite.config.ts 中设置覆盖率阈值

{
  "lint-staged": {
    "*.{vue,tsx}": ["vitest related --run --coverage"]
  }
}

源码地址:gitee.com/jesse131/vu…

参考文章

  1. Vitest 官网