Link

익스텐션 테스트 하기

Visual Studio Code는 당신이 만든 익스텐션의 실행 및 디버깅을 제공해 줍니다. 모든 테스트는 익스텐션 개발 호스트라고 불리는 VS Code에 있는 특별한 인스턴스에 의해 진행되고, 이는 모든 VS Code API에 접근이 가능합니다. 이러한 테스트들은 VS Code 인스턴스를 이용한 단위 테스트 없이도 가능한 테스트이기 때문에, 이를 통합 테스트라고 부릅니다. 이 문서는 VS Code 통합 테스트에 집중해 설명한 문서입니다.

개요

_만약 당신이 vscode로부터 마이그레이션 했다면, vscode로부터 마이그레이션 하기를 먼저 보세요.

만약 당신이 Yeoman Generator를 이용해 익스텐션을 스캐폴딩 했다면, 통합 테스트는 이미 당신을 위해 만들어져 있을 것입니다.

만들어진 익스텐션에서 npm run test 또는 yarn test 를 이용해 다음과 같은 통합 테스트를 할 수 있습니다:

  • 최신 버전의 VS Code를 다운로드하고 압축 해제합니다.
  • 익스텐션 테스트 스크립트에서 지정한 Mocha 테스트를 실행합니다.

또는, 이 가이드를 위한 설정을 helloworld-test-sample에서 찾을 수 있습니다. 이 문서의 남은 부분은 이 예제 파일에 대해 설명하고 있습니다:

테스트 스크립트

VS Code는 익스텐션 테스트를 위해 --extensionDevelopmentPath--extensionTestsPath 두 개의 CLI 매개 변수를 제공해 줍니다,

예시:

# - 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)는 익스텐션 테스트 매개 변수와 함께 다운로드, 압축 해제, VS Code 실행을 간소화 하기 위해 vscode-test API를 사용합니다:

import * as path from 'path';

import { runTests } from 'vscode-test';

async function main() {
  try {
    // 익스텐션 매니페스트 package.json를 포함하는 폴더
    // --extensionDevelopmentPath에 전달됩니다.
    const extensionDevelopmentPath = path.resolve(__dirname, '../../../');

    // 익스텐션 테스트 실행 스크립트의 경로
    // --extensionTestsPath에 전달됩니다.
    const extensionTestsPath = path.resolve(__dirname, './suite/index');

    // VS Code를 다운로드 받고, 압축을 풀고, 통합 테스트를 시행합니다.
    await runTests({ extensionDevelopmentPath, extensionTestsPath });
  } catch (err) {
    console.error(err);
    console.error('Failed to run tests');
    process.exit(1);
  }
}

main();

이외에도, vscode-test API 는 다음과 같은 것들을 지원합니다:

  • 특정 작업 공간에서 VS Code를 실행
  • 가장 최신 안정 버전 대신 다른 버전의 VS Code 다운로드
  • VS Code 를 추가적인 CLI 매개 변수와 함께 실행

추가적인 API 사용 예제는 microsoft/vscode-test에서 찾을 수 있습니다.

테스트 실행 스크립트

통합 테스트를 실행할때, --extensionTestsPath는 테스트 묶음을 실행하는 테스트 실행 스크립트 (src/test/suite/index.ts)를 가르킵니다. 아래는 Mocha로 테스트 묶음을 실행하기 위해 사용하는 helloworld-test-sampletest runner script입니다. 이 예제를 시작점으로 잡고 Mocha’s API를 이용해 다양하게 수정할 수 있습니다. 프로그래밍 방식으로 작동되는 다른 테스트 프레임워크를 Mocha 대신에 이용할 수도 있습니다.

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

export function run(): Promise<void> {
  // Mocha 테스트를 생성합니다.
  const mocha = new Mocha({
    ui: 'tdd'
  });
  mocha.useColors(true);

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

  return new Promise((c, e) => {
    glob('**/**.test.js', { cwd: testsRoot }, (err, files) => {
      if (err) {
        return e(err);
      }

      // 파일들을 테스트 묶음에 추가합니다.
      files.forEach(f => mocha.addFile(path.resolve(testsRoot, f)));

      try {
        // Mocha 테스트를 실행합니다.
        mocha.run(failures => {
          if (failures > 0) {
            e(new Error(`${failures} tests failed.`));
          } else {
            c();
          }
        });
      } catch (err) {
        e(err);
      }
    });
  });
}

테스트 실행 스크립트와 *.test.js파일들은 모두 VS Code API에 접근할 수 있습니다.

여기에 실행 예제가 있습니다. (src/test/suite/extension.test.ts):

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

// 'vscode' 모듈에서 필요한 API를 모두 가져올 수 있습니다.
// 뿐만 아니라 당신의 익스텐션 또한 가져와 테스트 할 수 있습니다.
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.equal(-1, [1, 2, 3].indexOf(5));
    assert.equal(-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를 통해 진행한다면, 이런 오류가 날겁니다:

익스텐션 테스트를 명령 창에서 실행하는 것은 다른 코드 인스턴스가 실행중이지 않을 때만 가능합니다.

이를 해결하기 위해 VS Code Insiders를 개발에 이용하거나 이러한 제약을 우회하기 위해 디버그 실행 설정을 이용해 익스텐션을 실행할 수도 있습니다.

디버깅 도중 다른 익스텐션 비활성화

VS Code에서 익스텐션 테스트를 디버깅 할 때, VS Code는 설치되어 있는 모든 인스턴스를 사용하고, 모든 확장 프로그램을 로드시킵니다. --disable-extensions설정을 launch.json에 추가하거나 vscode-testrunTests 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,
  /**
   * `--extensionDevelopmentPath`를 포함해, VS Code 실행 파일에 전달된 매개 변수의 리스트,
   * 그리고 `extensionDevelopmentPath`와 `extensionTestsPath`에서 제공하는 `--extensionTestsPath`
   * 옵션들입니다.
   *
   * 만약 첫번째 매개 변수가 파일/폴더/작업공간의 경로라면, 실행된 VS Code 인스턴스가
   * 열리게 될겁니다.
   *
   * `code --help`를 통해 가능한 매개 변수를 확인하세요.
   */
  launchArgs: ['--disable-extensions']
});

vscode-test를 통한 사용자 지정 설치

가끔씩 테스트를 시작하기 전에 다른 익스텐션을 설치하기 위해 code --install-extension를 실행하는 것처럼, 사용자 지정 설치를 해야 할 때가 있습니다. vscode-test는 보다 세분화된 API를 이용해 이런 경우를 처리합니다:

import * as cp from 'child_process';
import * as path from 'path';
import {
  downloadAndUnzipVSCode,
  resolveCliPathFromVSCodeExecutablePath,
  runTests
} from 'vscode-test';

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 = resolveCliPathFromVSCodeExecutablePath(vscodeExecutablePath);

    // 사용자 지정 설치를 위해 cp.spawn / cp.exec 를 사용합니다.
    cp.spawnSync(cliPath, ['--install-extension', '<EXTENSION-ID-OR-PATH-TO-VSIX>'], {
      encoding: 'utf-8',
      stdio: 'inherit'
    });

    // 익스텐션 테스트를 시행합니다.
    await runTests({
      // 지정된 `코드` 실행 파일을 사용합니다.
      vscodeExecutablePath,
      extensionDevelopmentPath,
      extensionTestsPath
    });
  } catch (err) {
    console.error('Failed to run tests');
    process.exit(1);
  }
}

main();

vscode로부터 마이그레이션하기

vscode 모듈은 익스텐션 통합 테스트를 실행할 때 기본적으로 사용되던 방법이었지만, 현재는 vscode-test로 대체되었습니다. 마이그레이션을 하는 방법은 다음과 같습니다:

  • vscode dependency를 삭제합니다.
  • vscode-test dependency를 만듭니다.
  • 오래된 vscode모듈 또한 VS Code 타입 정의를 다운로드하는데 사용되었으므로, 다음을 수행해야 합니다.
    • package.json에 있는 engine.vscode를 따라 수동으로 @types/vscode를 설치해야 합니다. 예를 들어, engine.vscode1.30버전이라면, @types/vscode@1.30를 설치하면 됩니다.
    • package.json"postinstall": "node ./node_modules/vscode/bin/install"를 삭제합니다.
  • test script를 추가합니다. runTest.ts의 예제를 이용해도 됩니다.
  • runTest.ts를 컴파일하기 위해 package.jsontest스크립트를 지정합니다.
  • test runner script를 추가합니다. sample test runner script의 예제를 이용해도 됩니다. vscodemocha@4glob에 의존해 사용되었으므로, 이제는 devDependencies의 일부로써 그것들을 설치해야 합니다.

다음 단계

  • 지속적 통합(CI) - Azure DevOps같은 지속적 통합(CI) 서비스를 이용해 익스텐션 테스트를 실행해 보세요.