Vim でスクリプトローカル関数もユニットテストしたい!

この記事は Vim Advent Calendar 2012 221日目の記事になります。


Twitter を徘徊していたらこんなツイートを見かけました。


これには激しく同意で、わたしもグローバル関数なんていう副作用バリバリの関数をテストするよりも細々としたスクリプトローカル関数をテストしたいことが多いです。
しかし、スクリプトローカル関数は通常、外部から呼び出すことが出来ないので、スクリプトローカル関数をテストしたい場合は必然的にそのファイル内でテストコードを既述する必要が出てきます。
が、実コード内にテストコードは書きたくありません。


と、いうことで owl.vim を使用して任意のファイルのスクリプトローカル関数を別のファイルから呼び出してユニットテストを行なってみたいと思います。

[owl.vim とは]

わたしが作っているオレオレユニットテストプラグインです。
最近は開発が滞っていますが
owl.vim の簡単な説明はここら辺を読んでみて下さい。
開発中といっても今後そこまで大きな仕様変更はないはず。多分。


ちなみに Vim script のテストプラグインはいくつかありますが、"外部のスクリプトローカル関数をテストする"機能を持っているプラグインは知りません。知らないだけかも知れませんが。
あと低レベルなテストを目指したので自動テストとか Vim外部からのテストを実行とかそういう面白ギミックはないです

[インストール]

owl.vim の他に依存しているプラグインがいくつかあるのでそれもインストールしておく必要があります。

NeoBundle "osyo-manga/vim-owl"
NeoBundle "osyo-manga/vim-budou"
NeoBundle "osyo-manga/vim-chained"

[使い方]

簡単な使い方。

function! s:plus(a, b)
    return type(a:a) == type("") && type(a:b) == type("") 
\        ? a:a . a:b
\        : a:a + a:b
endfunction


" テストコードを記述するスクリプトローカル関数を定義する
" s:test_{名前} 名で定義
function! s:test_plus()
    " OwlCheck に渡した式を評価して 0 なら失敗、0以外なら成功
    " OwlCheck には
    "- グローバル変数
    " - ローカル変数
    " - スクリプトローカル関数
    " を渡すことが出来る

    " OK
    OwlCheck s:plus(1, 2) == 3

    " NG
    OwlCheck s:plus(1, 2) == -3
endfunction

" テストを実行する
" :OwlRun


こんな感じでテストを行うスクリプトローカル関数を用意し、その中で :OwlCheck 等でテストを行う式を書いていきます。
特徴的なのが :OwlCheck でスクリプトローカル関数を呼び出す事が出来ます。
これを利用する事で autoload 内のスクリプトローカル関数のテストを行う事が出来ます。

[外部のスクリプトローカル関数をテスト]

さて、実際に外部のスクリプトローカル関数を呼び出してみたいと思います。
今回は以下のような autoload ファイルに書かれているスクリプトローカル関数をテストしてみたいと思います。

[autoload/hoge.vim]

function! s:uniq(list)
    return filter(a:list, "count(a:list, v:val) <= 1")
endfunction

function! s:uniq_sort(list)
    return sort(s:uniq(a:list))
endfunction


上記の autoload にあるファイルをテストする場合は次のような形になります。

[test.vim]

function! s:test_hoge()
    " autoload/hoge.vim のスクリプト固有の番号を取得
    let owl_SID = owl#filename_to_SID("autoload/hoge.vim")

    " autoload/hoge.vim 内のスクリプトローカル関数が呼び出される
    OwlCheck s:uniq([1, 1, 1, 1]) == [1]
    OwlCheck s:uniq([3, 2, 2, 1]) == [3, 2, 1]
    OwlCheck s:uniq([3, 3, 2, 2, 1, 1, 1, 1]) == [3, 2, 1]
    OwlCheck s:uniq([]) == []

    OwlCheck s:uniq_sort([1, 1, 1, 1]) == [1]
    OwlCheck s:uniq_sort([3, 2, 3, 2, 1]) == [1, 2, 3]
    OwlCheck s:uniq_sort([3, 2, 1, 2, 1, 1, 1]) == [1, 2, 3]
    OwlCheck s:uniq_sort([]) == []
endfunction

" :OwlRun でテストを実行


こんな感じです。
ローカル変数 owl_SID に "スクリプト固有の番号" を設定すると :OwlCheck はその番号のスクリプトローカル関数を呼び出すようになります。
また、スクリプト固有の番号は owl#filename_to_SID() を使用して比較的簡単に取得する事が出来ます。


と、いう感じで owl.vim について簡単に紹介してみました。
ぶっちゃけ『使い勝手とかどうなのよ』とか『ドキュメント薄くね』とかまだいろいろと問題はあるんですが気になる方は使ってみてみるといいかも知れませぬ。