Visual Studio Code拡張機能のテスト環境を構築する(TypeScript)
はじめに
先日紹介した自作のVisual Studio Code(以下VSCode)拡張機能の開発時に初めてTypeScriptを用いた拡張機能のテスト環境を構築したのですが、VSCodeの構成もよくわからないままに取り組み、随分苦労したので、今回はそれを解説してみたいと思います。
テストの実行方法の種類
公式には、テストの開発方法は下記にドキュメントがあります。
Testing Extension | Visual Studio Code Extension API
VSCode上で拡張機能をテストするには、コーディングに使用しているVSCodeとは別にVSCodeを立ち上げ、そのVSCode上で拡張機能を叩くテストプログラムを動かします。
この別のVSCodeのことを「インスタンス」と呼びます。
テストプログラム用のVSCodeインスタンスを立ち上げるには、実際にコマンドプロンプトなどからVSCodeを立ち上げる方法もありますが、他にもvscode-test
モジュール(以下vscode-test
)というnpmパッケージを読み込み、そこから子プロセスでVSCodeインスタンスを生成しテストを行うという方法もあり、今後主流になっていくそうです(下記のリストのうち2-1)。
ただ本稿では、参考にした拡張機能にならって、vscode-test
を使わない方法 を解説します。
vscode-test
を使わない方法- コマンドプロンプトで「
code ~
」のようにVSCodeの実行ファイルを指定して、VSCodeインスタンスを起動する。
もっともオーソドックスな方法です。
ここでいちいちオプションを指定するのが面倒なので、次の1-2の方法があります。 launch.json
にパラメータを記入して、使用中のVSCodeインスタンスからGUIを操作し、テスト用のVSCodeインスタンスを起動する。
1-1でいちいちコマンドプロンプトで指定しているオプションを、launch.json
に記述してVSCodeインスタンスを起動することが出来ます。
(本稿で解説します)
- コマンドプロンプトで「
vscode-test
を使う方法- NodeのプログラムとしてTypeScriptを記述し実行する。(例:
runTest.ts
)
その中でvscode-test
のrunTests
関数を呼び出す。
(これでVSCodeインスタンスを子プロセスで起動することができます。このrunTest.ts
のことを「Test Script」と呼びます。)
コマンドプロンプトで「node ./out/test/runTest.js
」のように、Nodeプログラムとして指定することで起動できます。
ただし、後述しますが色々と制限があります。(参照:runTest.jsでテストするときの注意)
公式ドキュメントでもこの方法を主に解説しています。リモートデバッギングやMicrosoftが運営するAzureDevOps上でCIが回せるようになるのが理由のようです??
Continuous Integration | Visual Studio Code Extension API
- NodeのプログラムとしてTypeScriptを記述し実行する。(例:
テスト環境の構築方法
繰り返しになりますが、本稿では launch.json
にパラメータを記入して、使用中のVSCodeインスタンスからGUIを操作し、テスト用のVSCodeインスタンスを起動する テスト方法について解説します。
テストプログラムはTypeScriptで記述します。
特にJavaScriptであることを意識しない限りは「TypeScriptプログラム」と称して説明します。
必要なnpmパッケージ
テストを実行するには、開発環境のnode_modules
フォルダ内に、npmパッケージをインストールする必要があります。
npmパッケージ | 概要 |
---|---|
mocha | テストフレームワークのパッケージ |
istanbul | カバレッジ測定に使用します。 |
remap-istanbul | カバレッジとTypeScriptファイルとのマッピングに使用します。 |
他にも、BDD用のアサート記法を提供するchaiモジュールなどもありますが、今回は割愛します。(assertモジュールを使います。)
インストール方法
解説に使用する環境は、公式のドキュメントでも解説している、yeomanで生成するテンプレートを使用します。
Your First Extension | Visual Studio Code Extension API
上記サイトの手順に従って、VSCodeの拡張機能のテンプレートを生成します。
具体的には、下記の手順になります。
コマンドプロンプトで下記を実行。
その後は対話形式で生成するテンプレートの内容を聞かれるので適当に入力します。
生成直後は、下記のファイル構成になります。

テンプレート生成直後ではカバレッジ測定なども行えないため、以降の手順で調整していきます。
調整前の内容は、最後の「脚注(テンプレート生成直後の調整前のソース一覧)」に記載しています。
この作業は下記サイトを参考にしました。
VSCode extension code coverage · Roman Peshkov
extension.test.tsファイルの調整
(調整前のextension.test.ts
はこちら)
拡張機能の本体であるextension.ts
をテストするテストプログラムです。
下記のように調整します。
①テンプレートそのままでは、mochaのTDD記法とBDD記法が混ざっていて無駄に複雑になっているので、BDD記法に統一します。
②extension.ts
の読み込みがコメントアウトされているので、有効化します。
なお、このテストプログラムはあくまでサンプルのため、extension.ts
の処理を呼び出していません。
実行すると、とりあえずassertが成功し、「テストが1件成功しました」というメッセージが表示されます。
index.tsファイルの調整
(調整前のindex.ts
はこちら)
GUIから起動されるindex.ts
は「Test Runner」と呼ばれ、テストプログラムを呼び出す役割を持っています。
これを下記のように書き換えます。
元のindex.ts
は配下の各テストプログラムを呼び出すだけでしたが、上記の様に書き換えることで、istanbulを利用しカバレッジ計測ができるようになります。
引用元のサイトに記載のものは、すでに非推奨になったコードも含まれていたため、一部手を加えています。(①の箇所)
41行目の②は、このindex.ts
が使用する設定ファイル(coverconfig.json
)へのパスです。
トランスパイルで出力されたindex.js
を起点とした相対パスを指定します。
とりあえずそのままコピペすれば動くはずです。
coverconfig.jsonファイルの作成
index.ts
で読み込む設定ファイルです。
開発環境の作業フォルダの直下に新規作成します。
カバレッジ測定結果の出力先などを定義しています。
こちらも相対パスで指定する必要があるため、分かりにくいので注意してください。
名称 | 概要 |
---|---|
relativeSourcePath | テストしたいjsファイルが格納されているフォルダを指定します。 トランスパイル後の index.js のある場所を起点にして、相対パスで指定します。テンプレートのフォルダ構成だと、テストプログラムも一緒にカバレッジ測定してしまいますが、とりあえずは動くので目をつぶります。 |
relativeCoverageDir | カバレッジ測定結果のHTMLファイルの出力先を指定します。 トランスパイル後の index.js のある場所を起点にして、相対パスで指定します。とりあえずそのままコピペすれば動くはずです。 |
launch.jsonファイルの調整
(調整前のlaunch.json
はこちら)launch.json
を下記のように書き換えます。
"name": "Run Extension"
要素のブロックは今回使いません。"name": "Extension Tests"
要素のブロックの--extensionTestsPath
を、以下のように書き換えています。
このオプションには、トランスパイル後のindex.js
が格納されるフォルダへのパスを指定しますが、テンプレート生成直後の状態では最後にindex
がついており、どのパターンでもうまくVSCodeインスタンスが起動しません。
(他の解説でも、全部このようなパス指定になっているんですけどね・・・)
--extensionTestsPath=${workspaceFolder}/out/test/suite/index
↓--extensionTestsPath=${workspaceFolder}/out/test/suite
package.jsonファイルの調整
(調整前のpackage.json
はこちら)package.json
を下記のように書き換えます。
vscode-test
を用いたrunTest.ts
から起動するパターンのテスト構成になっているため、scripts
要素から"test": "node ./out/test/runTest.js"
を削除します。
また、devDependencies
要素に必要なnpmパッケージを追加・削除します。vscode-test
モジュールも使用しないので削除し、代わりにvscode
モジュールを追加します。
またそれに伴い、@types/vscode
も削除します。
npmパッケージの再インストール
package.json
を編集したので、いったんnpm_modules
フォルダを削除して、各npmパッケージをインストールし直します。
runTest.tsファイルの削除
今回は使用しないため、見出しの通りrunTest.ts
ファイルを削除します。
TypeScriptのトランスパイル
上記が全部完了したら、TypeScriptのトランスパイルを実行します。
すでにout
フォルダが生成されているときは、念の為、一旦削除してください。
out
フォルダに必要なJavaScriptファイルが出力されます。
調整作業後の確認
これでテストを実行する準備が整いました。
最終的には下記のような構成になります。

行った作業をまとめると、
launch.json
、index.ts
、package.json
、extension.test.ts
を編集coverconfig.json
を新規作成runTest.ts
を削除node_modules
フォルダを削除→npm install
で再生成- TypeScriptのトランスパイルを実行し、
out
フォルダを生成
となります。coverage
フォルダはこの後のテスト実行で生成されます。
テストの実行
デバッグサイドバーの「デバッグ開始」ボタンを使用します。

テストが実行され、

カバレッジ測定結果がcoverage
フォルダにHTMLで出力されます。

これでカバレッジ測定が可能なテスト環境が構築できました。
あとはテストをextension.test.ts
に書いていきます。
補足的な話
runTest.tsとは?
公式ドキュメントでは、「Test Script」と呼ばれています。
本稿では本ファイルは削除しますが、最近のVSCode拡張機能のテストはrunTest.ts
でvscode-test
を用いてVSCodeインスタンスを起動してテストを実行します。
仕組みとしては、Nodeのchild_process.spawnSync()
関数でVSCodeの実行ファイルに起動をかけ、runTest.ts
の子プロセスとしてVSCodeインスタンスを起動します。
このときさらに、コード内でvscode-test
に用意された機能を使うと、任意のバージョンのVSCodeをダウンロードして、そこの実行ファイル(Code.exe)に対して起動をかけることも出来ます。
これにより、バージョンを指定したVSCode上でテストを行うことができテスト環境を柔軟に構築することが出来るようになります。
またさらに、VSCodeインスタンスを起動する前に、他の拡張機能のインストールを指定することもできるため、特定の拡張機能がインストールされたVSCodeインスタンスを生成することも出来ます。
(ちなみに、ダウンロードしてきたVSCodeは、開発環境の.vscode-test
フォルダに保存されます)
launch.json
やCLIで単純に起動パラメータを指定して実行するよりも、より柔軟で統合的なテストが行えます。
runTest.tsでテストするときの注意
このように便利なvscode-test
を用いたテストですが、実は制限もあり、同一PC上でVSCodeインスタンスを起動することが出来ません。
本末転倒のようですが、普通に「タスクの実行」やデバッグサイドバーからrunTest.ts
を実行しようとすると、「Running extension tests from the command line is currently only supported if no other instance of Code is running.」とエラーが表示され、起動することが出来ません。
エラーメッセージの通り、CLIからは確かに起動できるのですが、それでも他にVSCodeが立ち上がっていると同じエラーで起動することが出来ません。
このため、テスト実行をするたびに毎回、今開発に使用しているVSCodeを終了させる必要があります。
これを解決する方法もあるにはあります。
VSCodeには、標準のVSCode以外にInsiderバージョンというものがあります。
このバージョンからrunTest.ts
に「タスクの実行」やデバッグサイドバーから起動をかけると、テストを実行することができます。
runTest.ts
内でダウンロードするVSCodeは標準のVSCodeのため、Insiderバージョンとは違うアプリと認識され、起動ができるようです。
拡張機能の開発のためにわざわざInsiderバージョンをインストールして使用しないといけないなんて、あまりに本末転倒すぎてにわかに信じられないのですが、本当にこの理解であっているのか誰か教えていただけると嬉しいです。
あくまでCI上で動かすことを想定しているが故の制限なんですかね・・・。
テスト起動時のTypeScriptのトランスパイル
yeomanからテンプレート環境を生成時は、テスト実行前に自動で「tsc -watch -p ./
」が実行されます。launch.json
内の"preLaunchTask": "npm: watch"
でpackage.json
上の「"watch": "tsc -watch -p ./"
」が起動されるためです。
ただ、おそらくインクリメンタルコンパイルしていて、うまくテストが動かないときがあるので、テスト起動前には普通に「tsc -p ./
」を実行するほうが無難です。
(これに、メチャクチャハマりました…)
テストにブレークポイントを張りたい
本稿の方法だと、テストプログラム(extension.test.ts
の方)には、ブレークポイントが効いてくれません。
そこで、下記のようにdebugger
を仕込むことで、テストプログラムを途中で止めてステップ実行することが出来るようになります。

本来は、package.json
にオプションを指定したりするようなのですが、私が試したときはうまくいかなかったので、このやり方のほうが確実です。
引用元・参考一覧
istanbulを用いた具体的なテスト構成の構築方法
VSCode extension code coverage · Roman Peshkov
(公式ドキュメント)VSCodeの拡張機能用テスト方法の説明
Testing Extension | Visual Studio Code Extension API
(公式ドキュメント)VSCodeのデバッグ方法。拡張機能の開発には特化していませんが、リモートデバッギングの話とかも載っています。
Debugging in Visual Studio Code
(公式ドキュメント)launch.jsonの各パラメータに指定する変数一覧。パス指定で迷子になったときに…。
Visual Studio Code Variables Reference
runTest.ts
って何者?というStack Overflow上の質問です。runTest.ts
(Test Script)とindex.ts
(Test Runner)の役割の違いが、公式ドキュメントだけではよくわからないため、stackoverflowにも質問が上がっていました。
javascript – runTest.ts class in vscode-test setup gets never used even in example project, what is it’s use? – Stack Overflow
vscode-test
がVSCodeインスタンスを子プロセスで生成するプロセスについて参考
node の spawn に関して調べてみた
node の spawn に関して調べてみた その2
脚注(テンプレート生成直後の調整前のソース一覧)
テンプレート生成直後のextension.test.ts
調整前のテンプレート生成直後のextension.test.ts
です。
テンプレート生成直後のindex.ts
調整前のテンプレート生成直後のindex.ts
です。
テンプレート生成直後のlaunch.json
調整前のテンプレート生成直後のlaunch.json
です。
テンプレート生成直後のpackage.json
調整前のテンプレート生成直後のpackage.json
です。
本記事は、チームスピリット Advent Calendar 2019に投稿した記事を加筆・再編集したものです。
TeamSpirit Advent Calendar Day13:Visual Studio Code拡張機能のテスト環境を構築する
ディスカッション
コメント一覧
まだ、コメントがありません