VSCode: 使用 executeCommand 打开示例项目后测试不会继续

VSCode: Tests won't proceed after using executeCommand to open a sample project

我正在尝试将一些单元测试添加到我正在开发的 Visual Studio 代码扩展中。我已经按照此处描述的设置扩展测试的方法进行操作:https://code.visualstudio.com/api/working-with-extensions/testing-extension

然而,这个秘诀并没有显示对实际测试扩展所做的任何有用的事情,只是进行设置的框架。

对于我想做的测试,我想做的第一件事就是打开一个示例项目。这就是我被困的地方。这是众多变化中的一种我尝试打开一个项目文件夹:

import assert from 'assert';
import { after, before, it } from 'mocha';
import path from 'path';
import { commands, Extension, extensions, Uri, window } from 'vscode';

suite('Extension Tests', () => {
  let extension: Extension<any>;
  const projectFolder = Uri.file(path.join(__dirname, '../../../test/suite/sample-project'));

  window.showInformationMessage('Start all tests.');

  before(() => {
    extension = extensions.getExtension('kshetline.ligatures-limited');
    const cmd = commands.executeCommand('vscode.openFolder', projectFolder).then(
      () => console.log('opened'),
      () => console.log('didn\'t open'));
    console.log('before');
    return cmd;
  });

  after(() => {
    console.log('after');
  });

  it('should load and activate extension', () => {
    assert.ok(extension);
    assert.ok(extension.isActive);
  });

  it('second test', () => {
    assert.ok(true);
  });
});

如果我删除 executeCommand,两个测试 运行 和测试套件都会正确终止,测试 window 关闭。

如果我保留 executeCommand,有时两个测试都不会执行,有时只执行第一个测试。测试套件没有终止——测试 window 保持打开状态,我必须手动停止测试。

我尝试过的变体:

很难找到如何正确执行此操作的示例。我查看了一个又一个扩展的存储库,似乎对扩展进行测试并不是很流行。 VSCode 一般而言,扩展编写者显然不会经常为测试而烦恼。 (也许我遇到的麻烦就是原因?)

我可以看到示例项目确实打开了,而且我没有看到任何控制台错误表明测试卡住并且无法继续进行的原因。

由于我不完全清楚的原因,在单元测试 运行 时使用 vscode.openFolder 命令简直是一件坏事。我猜它以某种方式破坏了测试 window 和测试环境之间的连接。

幸运的是,在这种特殊情况下,我可以只打开一个文件夹,这可以在启动测试代码之前在我的“launch.json”配置中完成。

{
  "version": "0.1.0",
  "configurations": [
    {
      ...
    },
    {
      "name": "Test Extension",
      "type": "extensionHost",
      "request": "launch",
      "runtimeExecutable": "${execPath}",
      "args": [
        "--extensionDevelopmentPath=${workspaceFolder}",
        "--extensionTestsPath=${workspaceFolder}/out/test/suite",
        "${workspaceFolder}/test/suite/sample-project"  // <-- project to open goes here
      ],
      "outFiles": [
        "${workspaceFolder}/out/test/**/*.js"
      ],
      "preLaunchTask": "npm"
    },
  ]
}

一旦我通过打开这个示例项目来引导我的测试环境,我就可以安全地使用 vscode.open 命令从示例项目文件夹中打开特定文档,然后监控和测试我的扩展程序对这些文件做了什么。

不幸的是,当您打开或关闭文件夹时,vscode 会重新加载 window。这意味着扩展主机已重新启动并失去与调试器以及当前 运行ning 测试脚本的连接。这就是为什么您的 before 函数永远不会 returns 并且您的测试中途中止的原因。

此行为有一个 open issue,尽管与测试无关。

window 重新加载的原因是,切换工作区可能会改变 vscode 环境。设置可能会被覆盖,工作区信任发生变化,运行ning 扩展的整个状态和缓存无效,并且可能会激活不同的扩展。不幸的是,这不会重新加载 --extensionTestsPath 传递给 vscode 的测试,最有可能在您的测试代码触发 window 或扩展主机重新加载时防止无限循环。


在问题得到解决之前,您在启动时打开文件夹的方法是正确的。虽然,如果你只是做 Unit Tests 你可以只打开单个文件并直接调用你的命令/事件使用的函数。如果您正在进行端到端或类似用户的测试并且具有与工作区相关的 activationEvents,您可能无法打开文件夹。

我找不到任何文档来确认它,但根据我的经验,您的扩展程序默认启用,您可以直接调用任何命令,因此通常不需要打开工作区。


关于测试文档,我强烈建议遵循 guides on the vscode website。当您按照那里的建议使用 yeoman bootstrap 扩展程序时,您已经设置了测试 运行ner,包括一些示例代码。它还会生成可用于调试测试的 launch.json

有关更多示例,请查看 vscode 的内置扩展(例如 TypeScript Language Features) or some of microsofts own extensions。其中大多数包括复杂程度各异的测试。您可能会找到与您的设置相匹配的一个最好的。


示例:使用 vscode-test

中的 runTest

vscode 中的 runTest 函数接受多个选项,包括您在 launch.json:

中使用的参数
# From the typings of vscode-test

interface TestOptions {
    ...,
    /**
     * Absolute path to the extension root. Passed to `--extensionDevelopmentPath`.
     * Must include a `package.json` Extension Manifest.
     */
    extensionDevelopmentPath: string;
    /**
     * Absolute path to the extension tests runner. Passed to `--extensionTestsPath`.
     * Can be either a file path or a directory path that contains an `index.js`.
     * Must export a `run` function of the following signature:
     *
     * ```ts
     * function run(): Promise<void>;
     * ```
     *
     * When running the extension test, the Extension Development Host will call this function
     * that runs the test suite. This function should throws an error if any test fails.
     *
     */
    extensionTestsPath: string;
    /**
     * 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?: string[];
}

使用 yeoman 加载多个工作区并在其中加载 运行 特定测试,编辑 src/test/runTest.ts 脚手架应该相当容易。

const workspaceTests = [
    { testDir: '../your/tests', workspaceDir: '../your/workspace' },
    ...
]

async function main() {
  try {
    const extensionDevelopmentPath = path.resolve(__dirname, '../../');

    for (const { testDir, workspaceDir } of workspaceTests) {  
        const extensionTestsPath = path.resolve(__dirname, testDir);
        const workspacePath = path.resolve(__dirname, workspaceDir);

        // Run tests in the specified workspace
        await runTests({
            extensionDevelopmentPath,
            extensionTestsPath,
            launchArgs: [ workspacePath ]
        });
    }
  } catch (err) {
    console.error('Failed to run tests');
    process.exit(1);
  }
}