小程序最佳实践之『单测-Vitest』篇 👮

696 阅读3分钟

参考模板

0

tnpm i vitest vite @vitest/coverage-c8 -D
  1. package.json 必须
{
  "scripts": {
    "test": "vitest run",
    "coverage": "vitest run --coverage",
  }
}

  1. vitest.config.ts 必须
/// <reference types="vitest" />
import { defineConfig } from 'vite';
// import path from 'path';

export default defineConfig({
  test: {
    // environment: 'happy-dom',
    coverage: {
      statements: 90,
      branches: 90,
      functions: 90,
      lines: 90,
    },

    // 按需开启
    // deps: {
    //   inline: [
    //     /_@alipay_limo-core@\d+.\d+.\d+@@alipay/,
    //     /@alipay/limo-core@\d+.\d+.\d+/
    //   ],
    // },
    // setupFiles: ['./test/vitest-setup.js'], // 按需开启
  },
  resolve: { 
    alias: { 
      // 按需开启,如果你配置了 TS path alias `@/* => src/*`
      // 否则报错:Error: Failed to load url @/xxx (resolved id: @/xxx) in foo.ts. Does the file exist?
      // '@': path.resolve('src'),
      
      // smallfish 项目需要开启。虚拟路径需要做映射
      // 'smallfish:stdlib/lodash': path.resolve('node_modules/lodash'),
    },
  }, 
});
  1. ./test/vitest-setup.js 可选
// 遇到问题再配置!
global.my = {
  call: function mockCallInSetup() {},
}

const { JSDOM } = require('jsdom');

const dom = new JSDOM('', { url: 'http://localhost' });

global.window = dom.window;
global.document = dom.window.document;
Object.keys(global.window).forEach((property) => {
  if (typeof global[property] === 'undefined') {
    global[property] = global.window[property];
  }
});

WHY 从 jest 迁移到 vitest

建议:vitest 到 1.0.0 再全面迁移

为什么迁移看看吐槽 yuque.antfin.com/chair/blog/…

vitest/0.22.1,迁移后速度提升3倍,单测耗时从7-11s降低到2s,极大提高了编写单测的积极性以及ci的时间

建议直接按照vitest.dev/guide/migra…迁移,如果遇到问题可以参考这个 MR

FAQ

Cannot use import statement outside a module

小程序引入了包依赖了 esm 内的方法。导致报错。

解决

找到引起报错的包 vi.mock dependency 或引起错误的 src 文件。

如果根目录和 src 目录都有 node_modules 需要指定绝对路径 mock。

vi.mock('../../../src/pages/common-problem-main/common/service', () => {
  return {
    // 需要 mock 的方法放这里
  }
}

如何测试私有方法 - Rust in-source testing

当然要不要测试私有方法这也是一个话题,一般情况下只需测试公有方法除非极端情况。

场景:测试端内 h5 的 getEnv 方法,因为利用了 ap.call 需要 mock ap,ap 依赖 dom 和 window,导致测试很大一部分工作量到浪费在 mock,且有很多坑,但是我们实际想测试的只是正则匹配逻辑而已。

有两种解决办法:

(1)将正则匹配逻辑封装成组件 export 出来,但是会破坏 api 的完整性。或者放到库的 __utils目录,但是因为和 getEnv 交织很紧密且其他方法并未使用该逻辑,会导致逻辑分散。其次 getEnv 内部做了缓存导致只能测试一种环境,可以通过导出一个方法(reset)清除缓存但是会导致增加额外的 api,或者 isolation testing 但是 mocha 不支持,需要引入三方依赖 github.com/xkizer/moch… 五年前的 😅,或者一个测试case一个文件或手动切换注释大法?或使用 serveless 团队自己开发的 test-isolated.js 总感觉高射炮打蚊子,ROI 太低。

image.png

Mocha is also not free from problems (still all known to me are patchable). It also doesn't provide test run parallelization and proper isolation, but we have achieved that through script/test-isolated.js

github.com/serverless/…

故(2)使用 vitest 的『In-source testing』最合适,因为能测试到内部方法和状态,无需做繁琐 mock 工作,也能修改内部状态变量,测试不同的环境。

如何书写单测,此处略过详见其官方文档。

// rollup.config.js
+ import replace from '@rollup/plugin-replace'

export default {
  plugins: [
+   replace({
+     'import.meta.vitest': 'undefined',
+   })
  ],
  // other options
}

// TypeScript
// tsconfig.json
{
  "compilerOptions": {
    "types": [
+     "vitest/importMeta"
    ]
  }
}

seems to be an ES Module but shipped in a CommonJS package

并提示

// vitest.config.js
export default {
  test: {
    deps: {
      inline: [
        "_@alipay_limo-core@3.34.3@@alipay"
      ]
    }
  }
}

其实更好的写法是用正则表达式,能兼容 yarn 和版本号升级。

inline: [
  /_@alipay_limo-core@\d+.\d+.\d+@@alipay/,
  /@alipay/limo-core@\d+.\d+.\d+/
]

vitest run报错 cannot find module "node:path"

node 需要 >= v14.18.0,为了支持 node: 协议 protocol require

解决:

{
  "engines": {
    "install-node": "14.19"
    // "install-alinode": "6.8.1"
  }
}

如果上述仍然报错。利用 npx -p node@14 vitest run可以指定运行时 node 版本:

{
  "test": "npx -p node@14 vitest run",
}

虽然简单但是有个缺点,在更高版本的环境下比如非远端开发,本地版本已经高于 14了,也会先安装后执行,单测耗时会延长。故通过判定 node 版本号,有条件的运行:

package.json

{
    "test": "sh ./test/test.sh",
    "vitest": "node -v && vitest run",
    "coverage": "npm test -- --coverage",
}

./test/test.sh

#!/bin/bash

if node -v | grep 14.17 -q
then
  # 解决云端开发 node 版本太低无法执行单测问题
  echo '版本太低,vitest 要求 node 版本必须 >= 14.18;将使用合法版本的 node 运行'
  npx -p node@14 npm run vitest -- $@
else
  npm run vitest -- $@
fi