結論
Vimで開いているrspecファイルのカーソル位置にあるテストを、自分の開発環境のdocker compose上のrspecで、気軽に実行出来る様にしました。
- 結果はvim-dispatchを使って
quickfixに表示。 - あとは温かみのある手作り。
-
rspecに渡すパスは、docker-composeのパスに合うように、rspecの絶対パスから切り出して作成 - カーソル行を取得してテスト対象を指定
-
" docker-composeでのテスト用定義。環境が変わったら書き換える
function! RunLineSpec() abort
let absolute_path = expand("%:p")
let relation_path = absolute_path[stridx(absolute_path, "spec/"):]
let rspec_cmd = 'docker compose run {docker-composeのサービス名}
bundle exec rspec ' . relation_path . ':' . line(".")
execute 'Dispatch' rspec_cmd
endfunction
nmap <silent> <Leader>test :call RunLineSpec()<CR>
経緯
最近、なんでもVimでやりたい病にかかっています。
先日も発作的に「docker-composeで実行するrubyのRspecをVimから呼び出したい」と思い立ち、いろいろと検討をしてみました。
vim-rspec 導入
まずは vim-rspecがシンプルで便利そうだったから入れてみました。
とはいえ、元々素直な開発環境ではないので、何とか頑張って設定して動けばいいなぁという気持ちからのスタートです。
自分の開発環境は
- 編集は
WSL(ubuntu20.04)上のコードを、WindowsTerminalでVim - 実行環境は
WSLで立ち上げたdocker composeのサービス上に、コードをマウントして実行
という形にしてあります。
なので、VimとRspecは実行環境が別々になっていて、Vimからbin/rspecを呼んでも、テストは動いてはくれないのです。
幸い、vim-rspecには`実行するコマンドを変更する設定が用意されていたので、まずはこれを、自分の環境に合うように指定しました。
let g:rspec_command = "docker compose run {サービス名} bundle exec rspec {spec}"
{spec}には、カレントディレクトリからbufferへの相対パスが入る様です。
なのでVimを開く前にカレントディレクトリをプロジェクトルートに変更するという運用にする事にしました。
これによって、「docker compose runの際のコンテナ上のカレントディレクトリ=Vimを開いた際のカレントディレクト」となるので、{rspec}に入っているパスが、サービスのコンテナ上のカレントディレクトリからも見れる事になります。
これで、vim-rspecを用いた、docker compose経由の実行は可能になりました。
ついでにhttps://github.com/tpope/vim-dispatchも使って、結果をquickfixに表示するようにもしました。
let g:rspec_command = "Dispatch docker compose run {サービス名} bundle exec rspec {spec}"
それから、実際にテスト実行につかうのは、カーソル付近のテストの実行だけなので、以下のマッピングも追加しました。
nmap <silent> <Leader>test call :RunNearestSpec()<CR>
これで、個別テストがしたい時は、そのテストにカーソルを合わせて<space>(<Leader>をスペースにしてるので)testでそのテストだけが動くし、全体のテストがしたければ、gg<space>testで全テストが動く様になります。
vim-rspecの使用感としては、これで全く問題なく運用出来ていました。
カレントディレクトリの制約を取り除きたい
Vimを開いた後の使用感の問題は無くなったけれども、プロジェクトルートでVimを開かないとVim上からテストが出来ないストレスは残り、カレントディレクトリがどうであれ、ちゃんとテストを実行出来る様にならないかと考えはじめました。
bufferの絶対パスから、アプリケーションルート基準の相対パスを作り、docker-composeに送る事が出来れば実現出来そうです。
rspecは、アプリケーションルートの直下のspecディレクトリに入っているので、ここで絶対パスを区切れば、spec/からの相対パスが取れると考えました。
(アプリケーションルートより前にspecディレクトリがある様な場合の事はもう考えなくて良いかなと諦めました。)
function! RunLineSpec() abort
let absolute_path = expand("%:p")
let relation_path = absolute_path[stridx(absolute_path, "spec/"):]
let rspec_cmd = 'docker compose run {docker-composeのサービス名}
bundle exec rspec ' . relation_path
let g:rspec_command = "Dispatch docker compose run operator_web bundle exec rspec " . relation_path
call RunNearestSpec()
endfunction
nmap <silent> <Leader>test call :RunLineSpec()<CR>
これで、Vim側のカレントディレクトリに関係無く、rspecが実行出来る様になりました!
万事解決!
と思いきや、まだ課題がありまして。
この時点で、vim-rspecが提供する{rspec}がどこにも入っていないコマンドが出来上がっていることになってます。
なので、実行時のカーソル位置を受け取る場所もなくなってしまい、RunNearestSpec()を実行しているのに、対象テストだけでなく、ファイル内の全テストが実行されるようになってしまったのです。
これじゃあ気軽なテストとしては使い物にならないですね…。
仕方ない、カーソル行を渡す記述も追加して…と考え始めたんですけど、このあたりでふと気づきました。
「考えてみたらこれ、もうvim-rspecの機能は一つも使ってないな…」
じゃあ素のvimscriptで良いのでは
というわけで冒頭の結論の記述になりました。
(この時、作成したパスの変数をDispatchとつなげて展開させる方法がわからず、Slackのvim-jpではじめて質問を投げてみたんですけど、一瞬で返答を貰えて、大変ありがたかったです。
Vim界は強いし、vim-jpも強かった。
元々rspecは、全テスト実行か特定行の実行かしかしないんで、これで必要充分です。
vimの勉強にもなったし、良い経験でした。
今後もvimを使い続けるのであれば、人様のプラグインを参考にしながら、自分でカスタマイズしたscriptを書いた方が、幸せになれるという事も多そうですね。