Visual Studio Code で編集中のテストコードを実行する

TL;DR

Visual Studio Codeではエディタからのコマンド実行がTasksという仕組みでできてとても便利。

背景

最近エディタをVisual Studio Codeに変えた。エディタのこだわりはそんなになくて、Vim → Emacs → Atom といった順番で2年に一度くらい乗り換えている。だいたいどのエディタにもVimキーバーインドエクステンションがあるのでなんとかなる。

Visual Studio CodeはAtomと同じElectronベースだけど、なぜかAtomよりサクサク動くので気に入っている。Microsoftのリソースパワーに感謝するしかない。

テストの話に移ると、テストを書くのはもちろんソフトウェア品質の担保やリグレッションの防止が目的だけれども、TDD的な文脈では開発のリズムを作るのも目的の一つだ。リズムよくテスト実行しつつ開発するには、エディタで開いているテストをさくさく実行できるのが重要になる。いちいちテストのためにターミナルにもどったりしてまごつきたくない。

仕事でPerlを書いている自分は、がんばってエディタからPerlのテストを実行できるようにしている。EmacsVimでも設定してたし、Atomでも拡張書いたりしていた(参考:
AtomからPerlのテストを直接実行するくん)。

Visual Studio Codeに乗り換えるにあたっても、エディタからテスト実行できるのかが重要なポイントだったのだけど、ちょっと調べたら実現することができた。他のエディタに比べて簡単で、ちょっとうれしくなったので紹介してみる。Perlのテストを実行するという話題だけど、エディタからコマンド実行したいならなんでも適用できる。

やりたいこと

  • エディタで編集中のテストコードをエディタの画面内で直接実行して結果を確かめたい
  • Perlでよく使うTest::Class形式のテストの場合、個別のテストメソッドだけを実行する機能がある。その機能を利用して、エディタ内のカーソル場所付近に書いてあるテストのみを実行したい

できたもの

コマンドパレットから "タスク: テスト タスクの実行" を選んで、自分で定義した"run Perl test here" を実行すれば、カーソルのある場所のテストが実行される。以下のスクリーンキャプチャの例ではmultiplyという名前のテストだけを実行してる。

f:id:hakobe932:20171109223909g:plain

やりかた

標準機能として用意されているTasksを使えばできる。カスタムタスクを自分で定義すれば、任意のコマンドをエディタから直接実行できる。

tasks.json

プロジェクトのディレクトリ直下に .vscodeというディレクトリを作成し、その中にtasks.jsonという設定ファイルを作成する。開いているPerlのテストファイルを実行するのでよければ以下のように書く。

{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "run Perl test all",
            "type": "shell",
            "command": "carton exec -- prove -v ${relativeFile}",
            "group": "test",
            "presentation": {
                "reveal": "always",
                "panel": "shared"
            }
        }
    ]
}

各項目の意味はドキュメントを参照していただくのが良い。

大事なのはcommandで設定されている内容で、文字通りここに実行したいコマンドを書く。設定内ではいくつか変数が使えて $relativeFile は現在エディタで開いているファイルのプロジェクト内の相対パスが入っている。変数の一覧を見れば使える変数がわかる。

この段階では、開いているテストファイル全体を実行することはできるけど、特定のテストのみを選択して実行することはできないので、工夫をする。

カーソル位置になるテストを実行するためのwrapperスクリプト

ここは少しPerlの事情になるのだけど、PerlのTest::Class形式のテストでは、メソッドごとにテストを書くようになっており、以下のように環境変数を設定することでテスト全体ではなく、特定のテストメソッドのみを実行することができる。

# multiply という名前のテストのみを実行する
$ TEST_METHOD=multiply cartone exec -- prove -v t/Calc.t 

エディタから特定のテストメソッドのみを実行したい場合、エディタのカーソル付近のテストメソッドの名前を取得し、上記の様なコマンドを実行できれば良いということになる。

tasks.jsonの定義ではあまり複雑なことはできないので、wrapperスクリプトを用いて実現する。つまり、tasks.jsonはからは以下のようなコマンドを呼び出すことにし

carton exec -- \
  perl .vscode/exec/run_test_at.pl ${relativeFile} ${lineNumber}

このコマンドの中で、${relativeFile}から得られるファイル名と${lineNumber}から得られるエディタのカーソル位置の行番号をもとに、実行すべきテストメソッド名を見つける。その後、見つけたテストメソッド名を用いてテストを実行する。

wrapperスクリプトは、こういう感じの実装になる。泥臭い感じだけど、特別遅かったりすることもないしちゃんと動く。

Perlにかぎらず同様のテクニックを使えば、カーソル位置の情報を使ってコマンドの実行ができる。

最終的な形

こういう感じ になる。以下のリポジトリに、設定例をまとめてあるので参考にされたい。

github.com

さらなる工夫

Taskはメニューから選ばなくても直接キーボードショートカットに登録できる

.vscode/tasks.json はプロジェクトディレクトリ内に設定するので、リポジトリにコミットして共有してしまえばチーム内で設定を共有できて便利。

これはまだ自分でもやっていないのだけれども、Probrem matcherという仕組みがあって、コマンドの出力結果から問題箇所をエディタ上でハイライトできるような仕組みがある。これを利用すれば、テストが落ちたときに落ちた箇所をハイライトできてかっこいいはず。

感想

Atomで同じことをしようとしたときは拡張を書いたりする必要があってたいへんだったけど、標準機能で達成できたのは手軽でよかった。Visual Studio Codeはターミナルが内蔵されていて、こういうサポートが発展しやすいのだと思う。

Visual Studio Code は TypeScript を書いている人に人気という印象だけど、普通に開発環境としても良くできているし、なぜかさくさく動いていいので、試してみると意外と気にいるかもしれずオススメです。