现已推出!阅读 10 月份的新功能和修复。

测试扩展

Visual Studio Code 支持运行和调试扩展的测试。这些测试将在名为 **扩展开发主机** 的 VS Code 特殊实例中运行,并完全访问 VS Code API。我们将这些测试称为集成测试,因为它们超出了无需 VS Code 实例即可运行的单元测试。本文档重点介绍 VS Code 集成测试。

概述

如果您使用 Yeoman 生成器 来搭建扩展,则已为您创建集成测试。

在生成的扩展中,您可以使用 `npm run test` 或 `yarn test` 运行集成测试,这些测试会:

  • 下载并解压缩 VS Code 的最新版本。
  • 运行扩展测试运行器脚本指定的 Mocha 测试。

快速设置:测试 CLI

VS Code 团队发布了一个命令行工具来运行扩展测试。您可以在 扩展示例仓库 中找到一个示例。

测试 CLI 提供快速设置,还允许您使用 扩展测试运行器 轻松运行和调试 VS Code UI 的测试。CLI 在后台专门使用 Mocha

要开始使用,您首先需要安装 `@vscode/test-cli` 模块,以及 `@vscode/test-electron` 模块,该模块允许在 VS Code 桌面中运行测试。

npm install --save-dev @vscode/test-cli @vscode/test-electron

安装模块后,您将拥有 `vscode-test` 命令行,您可以将其添加到 `package.json` 中的 `scripts` 部分。

{
  "name": "my-cool-extension",
  "scripts": {
+   "test": "vscode-test"

`vscode-test` 在当前工作目录的相对位置寻找一个 ` .vscode-test.js/mjs/cjs` 文件。此文件提供测试运行器的配置,您可以在这里找到完整的定义 此处

常见选项包括:

  • **(必需)** `files` - 包含要运行的测试的模式、模式列表或绝对路径。
  • `version` - 用于运行测试的 VS Code 版本(默认值为 `stable`)。
  • `workspaceFolder` - 测试期间要打开的工作区路径。
  • `extensionDevelopmentPath` - 扩展文件夹的路径(默认值为配置文件的目录)。
  • `mocha` - 包含要传递给 Mocha 的其他 选项 的对象。

配置可能像这样简单:

// .vscode-test.js
const { defineConfig } = require('@vscode/test-cli');

module.exports = defineConfig({ files: 'out/test/**/*.test.js' });

... 或者更高级:

// .vscode-test.js
const { defineConfig } = require('@vscode/test-cli');

module.exports = defineConfig([
  {
    label: 'unitTests',
    files: 'out/test/**/*.test.js',
    version: 'insiders',
    workspaceFolder: './sampleWorkspace',
    mocha: {
      ui: 'tdd',
      timeout: 20000
    }
  }
  // you can specify additional test configurations, too
]);

如果您通过传递数组来定义多个配置,则当您运行 `vscode-test` 时,它们将按顺序运行。您可以通过 `label` 进行筛选,并使用 `--label` 标志(例如 `vscode-test --label unitTests`)单独运行它们。运行 `vscode-test --help` 以获取完整的命令行选项集。

测试脚本

CLI 设置好后,您可以编写和运行测试。测试脚本可以访问 VS Code API,并在 Mocha 下运行。这是一个示例 (src/test/suite/extension.test.ts)

import * as assert from 'assert';

// You can import and use all API from the 'vscode' module
// as well as import your extension to test it
import * as vscode from 'vscode';
// import * as myExtension from '../extension';

suite('Extension Test Suite', () => {
  suiteTeardown(() => {
    vscode.window.showInformationMessage('All tests done!');
  });

  test('Sample test', () => {
    assert.strictEqual(-1, [1, 2, 3].indexOf(5));
    assert.strictEqual(-1, [1, 2, 3].indexOf(0));
  });
});

您可以使用 `npm test` 命令运行此测试,或者在安装 扩展测试运行器 后,使用 VS Code 中的 **测试:运行所有测试** 命令运行此测试。您还可以使用 **测试:调试所有测试** 命令调试测试。

高级设置:您自己的运行器

您可以在 helloworld-test-sample 中找到此指南的配置。本文档的其余部分将解释这些文件在示例中的上下文。

VS Code 提供了两个 CLI 参数来运行扩展测试,`--extensionDevelopmentPath` 和 `--extensionTestsPath`。

例如:

# - Launches VS Code Extension Host
# - Loads the extension at <EXTENSION-ROOT-PATH>
# - Executes the test runner script at <TEST-RUNNER-SCRIPT-PATH>
code \
--extensionDevelopmentPath=<EXTENSION-ROOT-PATH> \
--extensionTestsPath=<TEST-RUNNER-SCRIPT-PATH>

**测试脚本** (`src/test/runTest.ts`) 使用 `@vscode/test-electron` API 简化了下载、解压缩和使用扩展测试参数启动 VS Code 的过程。

import * as path from 'path';

import { runTests } from '@vscode/test-electron';

async function main() {
  try {
    // The folder containing the Extension Manifest package.json
    // Passed to `--extensionDevelopmentPath`
    const extensionDevelopmentPath = path.resolve(__dirname, '../../');

    // The path to the extension test runner script
    // Passed to --extensionTestsPath
    const extensionTestsPath = path.resolve(__dirname, './suite/index');

    // Download VS Code, unzip it and run the integration test
    await runTests({ extensionDevelopmentPath, extensionTestsPath });
  } catch (err) {
    console.error(err);
    console.error('Failed to run tests');
    process.exit(1);
  }
}

main();

`@vscode/test-electron` API 还允许:

  • 使用特定工作区启动 VS Code。
  • 下载 VS Code 的不同版本,而不是最新的稳定版本。
  • 使用其他 CLI 参数启动 VS Code。

您可以在 microsoft/vscode-test 中找到更多 API 用法示例。

测试运行器脚本

运行扩展集成测试时,`--extensionTestsPath` 指向 **测试运行器脚本** (`src/test/suite/index.ts`),该脚本以编程方式运行测试套件。以下是 `helloworld-test-sample` 的 测试运行器脚本,它使用 Mocha 运行测试套件。您可以将其用作起点,并使用 Mocha 的 API 自定义您的设置。您还可以使用任何其他可以以编程方式运行的测试框架来代替 Mocha。

import * as path from 'path';
import * as Mocha from 'mocha';
import { glob } from 'glob';

export function run(): Promise<void> {
  // Create the mocha test
  const mocha = new Mocha({
    ui: 'tdd',
    color: true
  });

  const testsRoot = path.resolve(__dirname, '..');

  return new Promise((c, e) => {
    glob('**/**.test.js', { cwd: testsRoot })
      .then(files => {
        // Add files to the test suite
        files.forEach(f => mocha.addFile(path.resolve(testsRoot, f)));

        try {
          // Run the mocha test
          mocha.run(failures => {
            if (failures > 0) {
              e(new Error(`${failures} tests failed.`));
            } else {
              c();
            }
          });
        } catch (err) {
          e(err);
        }
      })
      .catch(err => {
        return e(err);
      });
  });
}

测试运行器脚本和 `*.test.js` 文件都可以访问 VS Code API。

这是一个示例测试 (src/test/suite/extension.test.ts)

import * as assert from 'assert';
import { after } from 'mocha';

// You can import and use all API from the 'vscode' module
// as well as import your extension to test it
import * as vscode from 'vscode';
// import * as myExtension from '../extension';

suite('Extension Test Suite', () => {
  after(() => {
    vscode.window.showInformationMessage('All tests done!');
  });

  test('Sample test', () => {
    assert.strictEqual(-1, [1, 2, 3].indexOf(5));
    assert.strictEqual(-1, [1, 2, 3].indexOf(0));
  });
});

调试测试

调试测试类似于调试扩展。

这是一个示例 `launch.json` 调试器配置

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Extension Tests",
      "type": "extensionHost",
      "request": "launch",
      "runtimeExecutable": "${execPath}",
      "args": [
        "--extensionDevelopmentPath=${workspaceFolder}",
        "--extensionTestsPath=${workspaceFolder}/out/test/suite/index"
      ],
      "outFiles": ["${workspaceFolder}/out/test/**/*.js"]
    }
  ]
}

提示

使用 Insiders 版本进行扩展开发

由于 VS Code 的限制,如果您使用 VS Code 稳定版本并尝试在 **CLI 上** 运行集成测试,则会抛出错误。

Running extension tests from the command line is currently only supported if no other instance of Code is running.

一般来说,如果您从 CLI 运行扩展测试,则测试运行的版本不能已经运行。作为解决方法,您可以在 VS Code Stable 中运行测试,并使用 VS Code Insiders 进行开发。只要您不是在 VS Code Insiders 中,而是在 VS Code Stable 中从 CLI 运行测试,此设置就可以正常工作。

另一种方法是从 VS Code 本身内部的调试启动配置运行扩展测试。这还有另外一个好处,就是您甚至可以调试测试。

调试时禁用其他扩展

当您在 VS Code 中调试扩展测试时,VS Code 将使用全局安装的 VS Code 实例,并加载所有已安装的扩展。您可以将 `--disable-extensions` 配置添加到 `launch.json` 或 `@vscode/test-electron` 的 `runTests` API 的 `launchArgs` 选项。

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Extension Tests",
      "type": "extensionHost",
      "request": "launch",
      "runtimeExecutable": "${execPath}",
      "args": [
        "--disable-extensions",
        "--extensionDevelopmentPath=${workspaceFolder}",
        "--extensionTestsPath=${workspaceFolder}/out/test/suite/index"
      ],
      "outFiles": ["${workspaceFolder}/out/test/**/*.js"]
    }
  ]
}
await runTests({
  extensionDevelopmentPath,
  extensionTestsPath,
  /**
   * A list of launch arguments passed to VS Code executable, in addition to `--extensionDevelopmentPath`
   * and `--extensionTestsPath` which are provided by `extensionDevelopmentPath` and `extensionTestsPath`
   * options.
   *
   * If the first argument is a path to a file/folder/workspace, the launched VS Code instance
   * will open it.
   *
   * See `code --help` for possible arguments.
   */
  launchArgs: ['--disable-extensions']
});

使用 `@vscode/test-electron` 进行自定义设置

有时您可能希望运行自定义设置,例如运行 `code --install-extension` 在开始测试之前安装另一个扩展。`@vscode/test-electron` 有一个更细化的 API 来满足这种情况。

import * as cp from 'child_process';
import * as path from 'path';
import {
  downloadAndUnzipVSCode,
  resolveCliArgsFromVSCodeExecutablePath,
  runTests
} from '@vscode/test-electron';

async function main() {
  try {
    const extensionDevelopmentPath = path.resolve(__dirname, '../../../');
    const extensionTestsPath = path.resolve(__dirname, './suite/index');
    const vscodeExecutablePath = await downloadAndUnzipVSCode('1.40.1');
    const [cliPath, ...args] = resolveCliArgsFromVSCodeExecutablePath(vscodeExecutablePath);

    // Use cp.spawn / cp.exec for custom setup
    cp.spawnSync(
      cliPath,
      [...args, '--install-extension', '<EXTENSION-ID-OR-PATH-TO-VSIX>'],
      {
        encoding: 'utf-8',
        stdio: 'inherit'
      }
    );

    // Run the extension test
    await runTests({
      // Use the specified `code` executable
      vscodeExecutablePath,
      extensionDevelopmentPath,
      extensionTestsPath
    });
  } catch (err) {
    console.error('Failed to run tests');
    process.exit(1);
  }
}

main();

下一步

  • 持续集成 - 在持续集成服务(如 Azure DevOps)中运行您的扩展测试。