読者です 読者をやめる 読者になる 読者になる

vimtest修正記録-Vim scriptテスト用にモック機能を書いてみたpart2

vim

vimtestについてはこちらVim scriptのテストを行うvimtestプラグインを書いた - ぼっち勉強会
モチベーション維持と振り返りのための記録

  • モック機能書いてみた

モック機能書いてみた

前回の雛形をもとにアレコレ考えて修正した
ソースは長くなってしまったのでGistに置いておくとして、使い方はこんな感じ
動作環境はMacだけ。他の環境で動くかは分からない

使い方の概要
  1. (必須)モックオブジェクトを生成する
    1. 対象コードが書かれているソースファイルを指定
  2. (必須)モックとする関数名を指定する
  3. (任意)必要があれば引数の指定や戻り値の指定をする
  4. (必須)期待する呼び出し回数を定義する
let s:mock = s:mock(expand('%:p'))
call s:mock.function('hello').with('World', 'Vim').return('hogehoge').once()
echo s:hello('world', 'vim')
call s:mock.assert()
  • モック対象となるサンプルコード
function! s:echo()
  echo 100
endfunction

function! s:hello(text1, text2)
  return 'Hello, ' . a:text1 . ' and ' . a:text2
endfunction
前提

以降のケースではすでにモックオブジェクトが生成済みとする

let s:mock = s:mock(expand('%:p'))
ケース1:引数や戻り値は特にいじらず、一度だけ呼ばれることを検証する
call s:mock.function('echo').once()
call s:echo()
call s:mock.assert()

複数回s:echoを呼ぶと失敗メッセージが出る
試しに3回呼んでみるとこうなる

100
100
100
Function echo called expected 1 but was 3

ケース2:実行された際の処理をフックし、一度だけ呼ばれることを検証する
" コールバック用の関数
function! s:echo_callback()
  echo 'callback dayo'
endfunction
call s:mock.function('echo').callback('echo_callback').once()
call s:echo()
" => callback dayo
call s:mock.assert()
call s:echo()
" => 100

mock.assert()した後はもとの定義に戻る

ケース3:例外を発生させるようにし、一度だけ呼ばれることを検証する
call s:mock.function('echo').throw('booo').once()
try
  call s:echo()
catch /booo/
  echo 'error dayo'
  " => error dayo
endtry
call s:mock.assert()
call s:echo()
" => 100
ケース4:引数の検証をし、一度だけ呼ばれることを検証する
call s:mock.function('hello').with('World', 'Vim').once()
echo s:hello('World', 'Vim')
"echo s:hello('World', 'Emacs')
" => Function hello args expected ['World', 'Vim'] but was ['World', 'Emacs'] 
call s:mock.assert()
ケース5:戻り値をフックし、一度だけ呼ばれることを検証する
call s:mock.function('hello').return('hogehoge').once()
echo s:hello('World', 'Vim')
" => hogehoge
call s:mock.assert()
echo s:hello('World', 'Vim')
" => Hello, World and Vim
ケース6:引数の検証と戻り値をフックし、一度だけ呼ばれることを検証する
call s:mock.function('hello').with('World', 'Vim').return('hogehoge').once()
echo s:hello('World', 'Vim')
" => hogehoge
call s:mock.assert()
echo s:hello('World', 'Vim')
" => Hello, World and Vim
ケース7:引数の検証と戻り値をフックする。呼び出し回数は検証しない
call s:mock.function('hello').with('World', 'Vim').return('hogehoge').any()
echo s:hello('World', 'Vim')
" => hogehoge
echo s:hello('World', 'Vim')
" => hogehoge
echo s:hello('World', 'Vim')
" => hogehoge
call s:mock.assert()
echo s:hello('World', 'Vim')
" => Hello, World and Vim

課題

現状のやつはvimtestに取り込むとして、このままだと辞書の関数がcall()で呼べないのでどうしようかな

サンプルコード

let s:dict = {}
function! s:dict.sample()
  return 'hogeee'
endfunction

let s:out = ''
redir => s:out
silent! function s:dict.sample
redir END
echo s:out
unlet s:out

関数名を表示させて、例えばそれが5360だった場合

:echo call('5360', [])
" => E725: 辞書用関数が呼ばれましたが辞書がありません: 5360

うーん、:functionの結果に出力されないし、辞書関数ってcall経由で呼べないのだろうか