测试扩展

Visual Studio Code 支持运行和调试您的扩展的测试。这些测试将在名为 扩展开发主机 的特殊 VS Code 实例中运行,并具有对 VS Code API 的完全访问权限。我们将这些测试称为集成测试,因为它们超越了可以在没有 VS Code 实例的情况下运行的单元测试。本文档重点介绍 VS Code 集成测试。

概述

如果您正在使用Yeoman Generator来搭建扩展,那么集成测试已经为您创建好了。

在生成的扩展中,你可以使用 npm run testyarn test 来运行集成测试,这些测试:

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

快速设置:测试CLI

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

测试CLI提供了快速设置,并且还允许您使用Extension Test Runner轻松运行和调试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 文件。该文件提供了测试运行器的配置,你可以在这里找到完整的定义 here

常见选项包括:

  • (必填) 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命令运行此测试,或者在安装Extension Test Runner后,在VS Code中使用Test: Run All Tests命令。你也可以使用Test: Debug All Tests命令调试测试。

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

您可以在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.

一般来说,如果你从命令行界面运行扩展测试,测试运行的版本不能已经在运行。作为一种解决方法,你可以在VS Code稳定版中运行测试,并使用VS Code Insiders进行开发。只要你不是在VS Code Insiders中从命令行界面运行测试,而是在VS Code稳定版中运行,这种设置就能正常工作。

另一种方法是从VS Code内部的调试启动配置中运行扩展测试。这还有一个额外的优势,即你甚至可以调试测试。

调试时禁用其他扩展

当你在VS Code中调试扩展测试时,VS Code会使用全局安装的VS Code实例,并加载所有已安装的扩展。你可以将--disable-extensions配置添加到launch.json@vscode/test-electronrunTests 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的持续集成服务中运行您的扩展测试。