vitest从0到1搭建测试框架

521 阅读2分钟

参考地址:

一、安装和配置插件

(一) 安装插件

npm i vitest happy-dom c8 @vitest/coverage-c8 @vue/test-utils -D

  • vitest 核心插件
  • happy-dom 模拟Web浏览器,以便用于测试的工具
  • c8 & @vitest/coverage-c8 用于展示测试覆盖率
  • @vue/test-utils 提供一个 mount 方法,用于实例化一个组件

(二) 配置

1、vite.config.ts

可以在 vite.config.tstest 属性下进行 vitest 的相关配置。配置之前我们需要在文件顶部配置 三条斜线 命令告诉编译器在编译过程中引入额外的文件。

/// <reference types="vitest" />
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue"
...
export default defineConfig(
    {
        ...
        test: {
            environment: "happy-dom",
            coverage: {
                provider: 'c8', // or 'c8'
                reporter: ['text', 'json', 'html'],
            },
        },

    }
)

2、package.json

 "scripts": {
    "test": "vitest",
    "coverage": "vitest run --coverage" // 生成覆盖率报告
  }

3、命令解释

1、执行一个特定的测试文件

npx vitest 文件名 vitest 会寻找 **/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}形式的文件。

2、执行所有的测试文件

npm run test

举个例子 测试 Icon 组件,icon.test.ts

  • describe 描述,describe 会形成一个作用域
  • it 断言
  • expect 期望
  • mount 用于实例化一个组件
import {describe,expect,it} from 'vitest'
import {mount} from '@vue/test-utils' // mount 用于实例化一个组件
import icon from '../src/components/icon'
describe('test Icon',function() {
    it('class should contain "ivu-icon-ios-add"',function() {
        const wrapper = mount(icon,{
            props:{
                type: 'ios-add'
            }
        })

        expect(wrapper.classes()).toContain('ivu-icon-ios-add')
    })

    it('style should contain "font-size: 24px;"',function() {
        const wrapper = mount(icon, {
            props: {
                size: 24
            }
        })

        expect(wrapper.attributes('style')).toBe('font-size: 24px;')
    })

    it('style should containe "color: red;"',function() {
        const wrapper = mount(icon,{
            props: {
                color: 'red'
            }
        })

        expect(wrapper.attributes('style')).toBe('color: red;')
    })
})

执行命令 npx vitest icon 就会执行这个测试文件。

二、复杂组件的测试

Button 组件的测试为例:button.test.js

import { describe, it, expect } from 'vitest';
import { mount } from '@vue/test-utils';
import button from '../src/components/button';

describe('test button', function () {
  it('html shuold be "BUTTON" ', function () {
    const wrapper = mount(button);
    expect(wrapper.element.tagName).toBe('BUTTON');
  });
  it('class contain "ivu-btn-default"', function () {
    const wrapper = mount(button);
    expect(wrapper.classes()).toContain('ivu-btn-default');
  });
  // https://cn.vitest.dev/guide/migration.html
  // 从 Vitest v0.10.0 开始,声明测试的回调样式被弃用。 你可以重写它们以使用 async/await 函数,或者使用 Promise 来模仿回调样式。
  it('should change loading state', () =>
    new Promise(async (done) => {
      const Component = {
        template: `
        <Button type="primary" :loading="loading" @click="onChangeLoading" ref="btnEl">loading</Button>
        `,
        components: {
          Button: button,
        },
        data() {
          return {
            loading: false,
          };
        },
        methods: {
          onChangeLoading() {
            this.loading = true;
          },
        },
      };
      const wrapper = mount(Component);
      await wrapper.vm.$el.click();
      wrapper.vm.$nextTick(() => {
        expect(wrapper.classes()).toContain('ivu-btn-loading');
        const $icons = wrapper.findAll('.ivu-icon');
        expect($icons.length).to.equal(1);
        expect($icons[0].classes()).toContain('ivu-load-loop');
        expect($icons[0].classes()).toContain('ivu-icon-ios-loading');
        done(true);
      });
    }));
  //  这样使用 终端 ui 界面显示 测试 通过,同时报错
  // it('should change loading state', async (done) => {
  //   const Component = {
  //     template: `
  //         <Button type="primary" :loading="loading" @click="onChangeLoading" ref="btnEl">loading</Button>
  //         `,
  //     components: {
  //       Button: button,
  //     },
  //     data() {
  //       return {
  //         loading: false,
  //       };
  //     },
  //     methods: {
  //       onChangeLoading() {
  //         this.loading = true;
  //       },
  //     },
  //   };
  //   const wrapper = mount(Component);
  //   await wrapper.vm.$el.click();
  //   wrapper.vm.$nextTick(() => {
  //     expect(wrapper.classes()).toContain('ivu-btn-loading');
  //     const $icons = wrapper.findAll('.ivu-icon');
  //     expect($icons.length).to.equal(1);
  //     expect($icons[0].classes()).toContain('ivu-load-loop');
  //     expect($icons[0].classes()).toContain('ivu-icon-ios-loadin');
  //     // done(true); // done 被废弃
  //   });
  // });
});