Journey

技術に関することと覚書と

vimでTDDしやすい環境を整えた

自分はコードを書くときにTDDをするので、vimから手軽にテストを回せる環境が必要でした。 なので、いままではvim-test + vim-dispatchを使用していたんですが、quickfixlistの挙動だったりがなんか微妙だなーと感じていたのでasyncrun.vimを使用して自分でいい感じの設定を作成することに成功しました。

実際の動作イメージ

f:id:ippachi1218:20201010233739g:plain
動作イメージ

vimrcでの設定

hi TestRed term=reverse ctermfg=252 ctermbg=52 guifg=#D9D9D9 guibg=#730B00
hi TestGreen term=bold ctermbg=22 guibg=#006F00

function! AsyncTestNearest() abort
  let g:async_test_running = 1
  call popup_close(get(g:, 'test_bar_popup', 0))
  exec "AsyncRun bundle exec rspec -b %:p:" . line(".")
endfunction

function! AsyncTestFile() abort
  let g:async_test_running = 1
  call popup_close(get(g:, 'test_bar_popup', 0))
  exec "AsyncRun bundle exec rspec %:p"
endfunction

function! AsyncTestAll() abort
  let g:async_test_running = 1
  call popup_close(get(g:, 'test_bar_popup', 0))
  exec "AsyncRun bundle exec rspec"
endfunction

function! AsyncTestLint() abort
  let g:async_test_running = 1
  call popup_close(get(g:, 'test_bar_popup', 0))
  exec "AsyncRun bundle exec rubocop"
endfunction

function! AsyncTestLintLocal() abort
  let g:async_test_running = 1
  call popup_close(get(g:, 'test_bar_popup', 0))
  exec "AsyncRun bundle exec rubocop %:p"
endfunction

let s:on_asyncrun_exit =<< trim END
  if !get(g:, 'async_test_running', 0)
    return
  endif

  cclose
  call popup_close(get(g:, 'test_bar_popup', 0))
  if g:asyncrun_status == "failure"
    let g:test_bar_popup = popup_create("", #{line: 10000, minwidth: 10000, highlight: 'TestRed'})
    botright cwindow 15
    execute "normal \<c-w>p"
  else
    let g:test_bar_popup = popup_create("", #{line: 10000, minwidth: 10000, highlight: 'TestGreen'})
  endif
END

let g:asyncrun_exit = join(s:on_asyncrun_exit, "\n")

完全にRSpec用となっていますが、簡単な修正で大体のテストコマンドに対応できるかなーと思います。

説明

Gifが全てなので細かい説明は省きますが、

  • :call AsyncTestNearest() でカーソル上のテストを実行
  • :call AsyncTestFile() で現在のテストファイルを実行
  • テスト失敗時はvimの一番下が赤くなり、quickfixlistを表示する
  • テスト成功時はvimの一番下が緑になり、quickfixlistを閉じる

みたいな感じです。

実現方法としてはAsyncRunが実行完了したときに実行される g:on_asyncrun_exit でいろいろやっている感じです。 現在のモードが隠れてしまったりと欠点があるものの、いまの自分には完全にマッチしていて満足しています。

おわり

プラグイン化しようとも思いましたが、現状AsyncRunを使用している人にとってやりづらいだろうなーと思って、もうちょっといい解決方法を考えているので、思いついたらプラグインにしたいです。