UTを使ってVim scriptのテストを書く
※追記:2012/4/1 自分でテスティングフレームワーク作成しました。
Vim scriptのテストを行うvimtestプラグインを書いた - ぼっち勉強会
最近Vim scriptの勉強を始めました。
:h script を見たりしながらプラグインを作成しようとしているのですが、
やはり何かを作るにはテストがないと安心できません。
テストなしに書き進められるほど、ぼくの実力は高くありません。
そこでVim scriptのテスティングフレームワークには何があるのか調べてみました。
結果としてUTが個人的に合っていたので使ってみることにしました。
その導入方法を簡単にですけど共有します。
テスティングフレームワークあれこれ
ググったりしてみると、以下のものが引っかかりました。
- runVimTests
- vimUnit
- vspec
- UT
- 後述
上記の中ではrunVimTestsとvimUnitを試してみました。
runVimTests
以下の理由から導入を辞めました。
vimUnit
- まだちゃんと出来上がっていないので却下
- helpがTODOだらけだったので、まだ導入するものではないのかと思って
vspec
xSpec的に書けそうな予感で興味はあるんですが、使い方分からなかった!
あ、でもちゃんとヘルプを読んだ記憶はないからヘルプを読めば分かるのかも
UTをインストール
そんなわけでVim script自体よく分かっていないので、まずはテストも簡単に書けるのがいいんです。
そういう意味ではUTがいい感じでした。
- UT本家(本体とlibで分かれてます)
- githubミラー
- intuited/lh-vim-ut · GitHub
- intuited/lh-vim-lib · GitHub
- ※注意 vim-scripts/lh-vim-lib · GitHub では古い。BundleSearchで見つかるのもこっち
Vundle管理の方は以下のようにしてvimrcに設定を書いて:BundleInstallです。
Bundle 'git://github.com/intuited/lh-vim-lib.git' Bundle 'git://github.com/intuited/lh-vim-ut.git'
Vimballの方は本家からそれぞれ落としてきてインストールしましょう。
pathogenやNeoBundleは分かりません!
動作確認してみる
適当なところに次のようなsample_test.vimを作成します。
UTSuite [sample] sample 日本語も大丈夫 Assert 1 == 1
UTSuiteはテストスイートの定義だと思います。
「UTSuite [名前]説明」かと。
AssertはxUnitでいうところのassertEqualsっぽいです。
ファイルを保存してコマンド「:UTRun %」を実行してみましょう。
テストが実行されてQuickFixに結果が保存されています。
「:cw」としてQuickFixを開いて以下のように表示されていればOKです。
sample_test.vim|| SUITE <[sample] sample 日本語も大丈夫> sample_test.vim|| SUITE<[sample] sample 日本語も大丈夫> 0/0 tests successfully executed.
テストを書いてみる
上記はただのAssert一つでした。
グローバルな箇所で記述することも可能ですが、
テスト関数を作らないと結果としてテストとは認識されません。
テスト関数はs:Test始まりもしくはs:test始まりじゃないとテストとして認識されないようです。
以下に簡単なサンプルを書いてみます。
UTSuite [sample] sample 日本語も大丈夫 function! s:Test1() Assert 1 == 1 endfunction
:UTRun %
sample_test.vim|| SUITE <[sample] sample 日本語も大丈夫> sample_test.vim|| SUITE<[sample] sample 日本語も大丈夫> 1/1 tests successfully executed.
ちゃんと「1/1 tests successfully executed.」としてテストが1件と認識されたようです。
失敗するテストを含めてみる
UTSuite [sample] sample 日本語も大丈夫 function! s:TestOK() Assert 1 == 1 endfunction function! s:TestNG() Assert 1 == 2 endfunction
:UTRun %
sample_test.vim|| SUITE <[sample] sample 日本語も大丈夫> sample_test.vim|8| assertion failed: 1 == 2 sample_test.vim|| SUITE<[sample] sample 日本語も大丈夫> 1/2 tests successfully executed.
失敗したテストのAssertが簡潔な内容で表示されました。
ここは、もうちょっと詳細なメッセージがあると嬉しいですね。
失敗したあとのテストは走るの?
UTSuite [sample] sample 日本語も大丈夫 function! s:Test1() Assert 1 == 1 endfunction function! s:Test2() Assert 1 == 2 endfunction function! s:Test3() Assert 1 == 1 endfunction
:UTRun %
sample_test.vim|| SUITE <[sample] sample 日本語も大丈夫> sample_test.vim|8| assertion failed: 1 == 2 sample_test.vim|| SUITE<[sample] sample 日本語も大丈夫> 2/3 tests successfully executed.
走っています。s:Test2で失敗したにも関わらずTest3が実行されて、
結果として「2/3 tests successfully executed.」となりました。
AssertとAssert!の違いは?
AssertではなくAssert!というのもあります。
これは失敗したらそのあとは実行しないよということだとヘルプにありました。
UTSuite [sample] sample 日本語も大丈夫 function! s:Test() echo "hey" Assert 1 == 1 echo "heyhey" Assert 1 == 2 echo "heyheyhey" endfunction
:UTRun %
hey heyhey heyheyhey
UTSuite [sample] sample 日本語も大丈夫 function! s:Test() echo "hey" Assert 1 == 1 echo "heyhey" Assert! 1 == 2 "ここで失敗するからあとは実行しない echo "heyheyhey" endfunction
:UTRun %
hey heyhey
とはいえ、そのテスト中の残りを実行しないということであって、
残りのs:Test関数を実行しない訳ではありません。
UTSuite [sample] sample 日本語も大丈夫 function! s:Test1() Assert 1 == 1 endfunction function! s:Test2() Assert! 1 == 2 endfunction function! s:Test3() Assert 1 == 1 endfunction
workspace/vimscript/sample_test.vim|| SUITE <[sample] sample 日本語も大丈夫> workspace/vimscript/sample_test.vim|8| assertion failed: 1 == 2 workspace/vimscript/sample_test.vim|8| Test <Test2> execution aborted on critical assertion failure workspace/vimscript/sample_test.vim|| SUITE<[sample] sample 日本語も大丈夫> 2/3 tests successfully executed.
簡単なhello関数とテストを書くとこんな感じになりました。
function! s:hello(...) let greet = "hello" return a:0 > 0 ? greet." ".a:1 : greet endfunction UTSuite [Hello] 引数なし function! s:TestHello() Assert s:hello() == "hello" endfunction UTSuite [Hello] 引数あり function! s:TestHello() let arg = "kanno" Assert s:hello(arg) == "hello " . arg endfunction
でもコマンド打ったりQuickFix開いたりするのは面倒だよ!
UTとは直接関係ないけど、設定を少し変えてより使いやすくしましょう!
QuickFixを自動で開く
以下の設定をvimrcに追記します。
autocmd QuickfixCmdPost make,grep,grepadd,vimgrep if len(getqflist()) != 0 | copen | endif
テストを保存時に自動実行する
以下の設定をvimrcに追記します。
autocmd BufWritePost *_test.vim execute ":UTRun %"
ファイルパターンはお好みで。
おまけ。注意(自分がハマった)
function! s:hello(...) endfunction UTSuite [Hello] 引数なし function! s:TestHello() Assert s:hello() == "hello" endfunction
これを実行するとどうなるか分かるでしょうか。
そうです、成功します。
sample_test.vim|| SUITE <[Hello] 引数なし> sample_test.vim|| SUITE<[Hello] 引数なし> 1/1 tests successfully executed.
Vim scriptでは空の関数は0を返します。
" :h 41.7 関数が ":endfunction" まで実行されたとき、あるいは引数無しで ":return" を使っ たときは 0 が返ります。
また、数値と文字列の比較では文字列が数値に変換されます。文字列が数値でない場合は0になります。
" :h 41.4 文字列と数値を比較するときは、文字列を数値に変換します。文字列が数字ではなかっ たときは 0 になるので注意してください。例: >
つまり、
s:hello()=>空の関数=>0
"hello"=>数値ではない文字列=>0
0 == 0 =>true
となりパスしてしまうのでした。
やってくれるぜ(ただの勉強不足)
UTのヘルプを見る
:h UT.txt です。
その他の機能とか
:h UT.txt です。
こうなるとさらにいいんだけどなあ
failedの詳細が知りたい
エラー時のメッセージが少なさすぎます。
ちゃんと動いているか不安なところもあり
次のようなテストを書きました。
固定で"hello"を返すので、引数を渡されても期待通りにはいきません。
function! s:hello(...) return "hello" endfunction UTSuite [Hello] 引数なし function! s:TestHello() Assert s:hello() == "hello" endfunction UTSuite [Hello] 引数あり function! s:TestHello() let arg = "kanno" Assert s:hello(arg) == "hello " . arg endfunction
sample_test.vim|| SUITE <[Hello] 引数なし> sample_test.vim|| SUITE <[Hello] 引数あり> sample_test.vim|13| assertion failed: s:hello(arg) == "hello " . arg sample_test.vim|13| assertion failed: s:hello(arg) == "hello " . arg sample_test.vim|| SUITE<[Hello] 引数あり> 0/2 tests successfully executed.
実行してみると「0/2 成功」とはおかしい。
一つ目のテストは通るはず。
その証拠に、二つ目のテストを削除するとパスします。
function! s:hello(...) return "hello" endfunction UTSuite [Hello] 引数なし function! s:TestHello() Assert s:hello() == "hello" endfunction
sample_test.vim|| SUITE <[Hello] 引数なし> sample_test.vim|| SUITE<[Hello] 引数なし> 1/1 tests successfully executed.