測試延伸模組
Visual Studio Code 支援為您的擴充功能執行與偵錯測試。這些測試將會在一個名為擴充功能開發主機 (Extension Development Host) 的特殊 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));
});
});
您可以使用 npm test 命令執行此測試,或在安裝 Extension Test Runner 後,於 VS Code 中使用 Test: Run All Tests 命令執行。您也可以使用 Test: Debug All Tests 命令來偵錯測試。
進階設定:您自己的執行器
您可以在 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();
測試工作區信任 (Workspace Trust) 行為
如果您的擴充功能在 package.json 中宣告了 capabilities.untrustedWorkspaces,請為受信任與不受信任的工作區分別新增整合測試。您無法從擴充功能測試中以程式設計方式授權或撤銷工作區信任。請針對受信任與不受信任的狀態使用分開的測試執行。
使用 @vscode/test-cli 時,請定義分開的測試設定,以便您可以個別執行每個信任狀態:
- trustedWorkspaceTests:提供一個未套用信任限制的基準執行。這有助於驗證您擴充功能的全功能行為,並捕捉受信任路徑中的迴歸問題。
- untrustedWorkspaceTests:在啟用工作區信任的情況下驗證「受限模式」行為。使用專用的
--user-data-dir可防止先前儲存的信任決策導致此執行意外被信任。
由於每個設定都有自己的 label,因此您可以個別執行它們(例如 vscode-test --label trustedWorkspaceTests 和 vscode-test --label untrustedWorkspaceTests),或是依序執行兩者。
// .vscode-test.js
const { defineConfig } = require('@vscode/test-cli');
const path = require('path');
module.exports = defineConfig([
{
label: 'trustedWorkspaceTests',
files: 'out/test/**/*.test.js',
workspaceFolder: './test/fixtures/trusted-workspace',
// Optional: disables Workspace Trust for this run
launchArgs: ['--disable-workspace-trust']
},
{
label: 'untrustedWorkspaceTests',
files: 'out/test/**/*.test.js',
workspaceFolder: './test/fixtures/untrusted-workspace',
// Keep Workspace Trust enabled and isolate user data for deterministic runs
launchArgs: [
'--user-data-dir',
path.join(__dirname, '.vscode-test', 'user-data-untrusted')
]
}
]);
在您的測試中,透過檢查 vscode.workspace.isTrusted 來斷言與信任相關的行為。
import * as assert from 'assert';
import * as vscode from 'vscode';
suite('Workspace Trust Tests', () => {
test('extension behavior changes by trust state', async () => {
const isFeatureAvailable = await vscode.commands.executeCommand<boolean>(
'myExtension.isRestrictedFeatureEnabled'
);
if (vscode.workspace.isTrusted) {
assert.strictEqual(isFeatureAvailable, true);
} else {
assert.strictEqual(isFeatureAvailable, false);
}
});
});
有關如何在擴充功能資訊清單中宣告信任需求,以及如何使用 vscode.workspace.isTrusted API 的詳細資訊,請參閱工作區信任擴充功能指南。
後續步驟
- 持續整合 (Continuous Integration) - 在 Azure DevOps 等持續整合服務中執行您的擴充功能測試。
- 工作區信任擴充功能指南 - 了解如何在您的擴充功能中宣告與處理工作區信任。