🚀 在 VS Code 中免費取得

測試擴充功能

Visual Studio Code 支援執行和偵錯你的擴充功能測試。這些測試將在名為擴充功能開發主機的特殊 VS Code 實例中執行,並具有完整的 VS Code API 存取權。我們將這些測試稱為整合測試,因為它們超越了可以在沒有 VS Code 實例的情況下執行的單元測試。本文件重點介紹 VS Code 整合測試。

概觀

如果你使用 Yeoman Generator 來建立擴充功能骨架,則已為你建立整合測試。

在產生的擴充功能中,你可以使用 `npm run test` 或 `yarn test` 來執行整合測試,這些測試會

  • 下載並解壓縮最新版本的 VS Code。
  • 執行擴充功能測試執行器腳本指定的 Mocha 測試。

快速設定:測試 CLI

VS Code 團隊發佈了一個命令列工具來執行擴充功能測試。你可以在擴充功能範例儲存庫中找到範例。

測試 CLI 提供快速設定,並且還允許你使用 Extension Test Runner 輕鬆執行和偵錯 VS Code UI 的測試。CLI 完全在底層使用 Mocha。

若要開始使用,你需要先安裝 `@vscode/test-cli` 模組,以及啟用在 VS Code Desktop 中執行測試的 `@vscode/test-electron` 模組

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));
  });
});

你可以在安裝 Extension Test Runner 後,使用 `npm test` 命令或在 VS Code 中使用 **測試:執行所有測試** 命令來執行此測試。你也可以使用 **測試:偵錯所有測試** 命令來偵錯測試。

進階設定:你自己的執行器

你可以在 helloworld-test-sample 中找到本指南的組態。本文檔的其餘部分將在範例的上下文中說明這些檔案

  • 測試腳本 (src/test/runTest.ts)
  • 測試執行器腳本 (src/test/suite/index.ts)

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 穩定版中執行測試,並使用 VS Code Insiders 進行開發。只要你不是從 VS Code Insiders 中的 CLI 執行測試,而是在 VS Code 穩定版中執行,此設定就能正常運作。

另一種替代方案是從 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) 中執行你的擴充功能測試。