持续集成测试
本地测试只证明"我的机器上能过"。插件要在三个操作系统上分发,CI 的价值就是每次提交都在 ubuntu、macos、windows 三套环境里完整跑一遍测试,把"只在我的机器上能过"变成"在哪都能过"。GitHub Actions 是开源插件最常用的选择。
工作流骨架:.github/workflows/ci.yml
仓库根目录下创建 .github/workflows/ci.yml。最简版本只跑单平台,先把链路打通:
name: CI
# 触发时机:push 到任意分支 + 手动触发
on:
push:
branches: [main, master]
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
# 安装依赖:npm ci 按 package-lock.json 精确安装
- name: Install dependencies
run: npm ci
# 运行测试(pretest 会先编译再跑)
- name: Run tests
run: npm test这个版本已经能跑:npm ci 安装依赖 → npm test 触发 pretest(编译 + lint)→ 编译产物运行 @vscode/test-electron 下载 VS Code 并执行测试。第一次跑会慢一些,因为要下载整套 VS Code。
三平台并行测试矩阵
插件要同时支持三大桌面平台,用 strategy.matrix 声明矩阵,GitHub 会并行起三个 job:
jobs:
test:
# 三平台并行:每个 runner 跑一遍完整测试
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
node: [20]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- name: Install dependencies
run: npm ci
# Linux 无头环境需要 xvfb 兜底,见下文
- name: Run tests (Linux)
if: runner.os == 'Linux'
run: xvfb-run -a npm test
- name: Run tests (macOS / Windows)
if: runner.os != 'Linux'
run: npm test| 矩阵维度 | 取值 | 说明 |
|---|---|---|
os | ubuntu-latest、macos-latest、windows-latest | 三个独立 job 并行执行 |
node | 20(可加 18) | 同时验证多个 Node 版本,数组内是笛卡尔积 |
矩阵展开后共 3 个 job(也可 2 平台 × 2 Node 版本 = 4 个)。任何一个平台失败,PR 都会标红。想要"部分平台失败不阻塞",可加 fail-fast: false,让其他平台继续跑完便于收集完整结果:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]Linux 无头环境:xvfb-run
GitHub 的 ubuntu runner 没有显示器,而 VS Code 是图形应用,启动时需要 X 服务。xvfb-run 提供一个虚拟显示:
- name: Run tests on Linux
if: runner.os == 'Linux'
# xvfb-run 创建虚拟 X server;-a 自动选择空闲 display 编号
run: xvfb-run -a npm test不套 xvfb-run 时常见报错:
The futex facility returned an unexpected error code或
Error: ENOENT: no such file or directory, open '.../SingletonLock'这些都是无显示环境启动 GUI 应用导致的。macOS 和 Windows 的 runner 自带桌面会话,无需此步。
注意 @vscode/test-electron 下载 VS Code 也需要时间,可以固定版本号并配合缓存(见下文),把每轮 CI 的时间压下来。
测试报告生成与上传
Mocha 默认在 stdout 输出文本报告,CI 里没法直接查看。两步改善:生成 JUnit XML 报告文件,再作为 artifact 上传供下载。
先在 package.json 里配置 mocha 报告器:
{
"devDependencies": {
"mocha": "^10.2.0",
"mocha-junit-reporter": "^2.2.1"
}
}src/test/suite/index.ts 中按环境切换报告器——只有 CI 环境才输出 XML 文件:
import * as path from 'path';
import Mocha from 'mocha';
import { glob } from 'glob';
export async function run(): Promise<void> {
// CI 环境下使用 JUnit 报告器,生成 XML 文件
const isCI = process.env.CI === 'true';
const mocha = new Mocha({
ui: 'tdd',
color: true,
reporter: isCI ? 'mocha-junit-reporter' : 'spec',
reporterOptions: isCI ? {
// 报告输出路径,job 之间互不冲突
mochaFile: path.resolve(__dirname, '../../test-results/test-results.xml')
} : undefined
});
const testsRoot = path.resolve(__dirname, '..');
const files = await glob('**/**.test.js', { cwd: testsRoot });
files.forEach((f) => mocha.addFile(path.resolve(testsRoot, f)));
try {
await new Promise<void>((resolve, reject) => {
mocha.run((failures) => {
if (failures > 0) {
reject(new Error(`${failures} tests failed.`));
} else {
resolve();
}
});
});
} catch (err) {
console.error(err);
throw err;
}
}CI 里上传报告产物:
- name: Upload test report
# 即使测试失败也要上传报告,方便排查
if: always()
uses: actions/upload-artifact@v4
with:
name: test-report-${{ matrix.os }}
path: test-results/if: always() 保证测试失败时报告依然上传。每个平台的报告用 matrix.os 命名隔离,下载后可整体查看。
npm ci 与缓存优化
CI 每次从零装依赖 + 下载 VS Code 是最大的耗时项。三处缓存可以显著提速:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
# 自动缓存 npm 依赖目录(node_modules + ~/.npm)
cache: npm
- name: Cache VS Code test binaries
uses: actions/cache@v4
with:
# @vscode/test-electron 的下载缓存目录
path: |
~/.vscode-test
${{ runner.os == 'Windows' && '~/.vscode-test' || '~/.vscode-test' }}
key: ${{ runner.os }}-vscode-test-${{ hashFiles('package-lock.json') }}
restore-keys: |
${{ runner.os }}-vscode-test-缓存说明:
| 缓存对象 | 目录 | 收益 |
|---|---|---|
| npm 依赖 | ~/.npm(setup-node 的 cache: npm 自动处理) | 省去每次下载依赖 |
| VS Code 测试版 | ~/.vscode-test | 省去每次下载 100MB+ 的 VS Code,收益最大 |
注意 @vscode/test-electron 的缓存目录跨平台路径一致(都是用户目录下的 .vscode-test),可以直接用一个 key 缓存。Windows runner 上路径写法相同。
完整 CI 配置
把以上片段整合成一份可用的 ci.yml:
name: CI
on:
push:
branches: [main, master]
pull_request:
workflow_dispatch:
jobs:
test:
name: Test on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
node: [20]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: npm
- name: Install dependencies
run: npm ci
# VS Code 测试版下载缓存(大幅加速重复运行)
- name: Cache VS Code
uses: actions/cache@v4
with:
path: ~/.vscode-test
key: ${{ runner.os }}-vscode-test
restore-keys: |
${{ runner.os }}-vscode-test
- name: Run tests (Linux)
if: runner.os == 'Linux'
run: xvfb-run -a npm test
- name: Run tests (macOS / Windows)
if: runner.os != 'Linux'
run: npm test
- name: Upload test report
if: always()
uses: actions/upload-artifact@v4
with:
name: test-report-${{ matrix.os }}
path: test-results/
- name: Upload VS Code logs (on failure)
if: failure()
uses: actions/upload-artifact@v4
with:
name: vscode-logs-${{ matrix.os }}
path: |
~/.vscode-test/user-data/logs/
if-no-files-found: ignore几个实用细节:
| 细节 | 作用 |
|---|---|
pull_request 触发 | 每个 PR 自动跑测试,合并前拦截问题 |
| 失败时上传 VS Code 日志 | 扩展宿主崩溃、测试启动失败时,日志是唯一的线索 |
if: failure() 上传 | 只在失败时多传一份诊断信息,省空间 |
fail-fast: false | 一个平台挂了,其余平台继续跑完,拿到全部失败信息 |
跑通 CI 后,仓库根目录会出现一行徽章,把测试状态直接展示在 README 上:
[](https://github.com/你的仓库名/actions/workflows/ci.yml)持续集成把"测试能跑"升级为"提交即验证":三平台并行、报告可下载、失败可诊断。测试代码的价值也因此在协作场景里真正放大——任何人的任何提交,都会被同一套标准检验。