vimtest修正記録-Vim scriptテスト用にモックのサンプルを書いてみた
vimtestについてはこちらVim scriptのテストを行うvimtestプラグインを書いた - ぼっち勉強会
モチベーション維持と振り返りのための記録
- モック出来るかも?
追記:最新版vimtest修正記録-Vim scriptテスト用にモック機能を書いてみたpart2 - ぼっち勉強会
モック出来るかも?
Big Sky :: vimでスクリプト内関数を書き換える
この記事を読んで、モック機能が出来るんじゃないかと思って勢いで書いてみた。
まだ雛形なので、アレコレ手直ししないといけないけどとりあえず。
- 呼び出し例
function! s:foo(text) return "Hello, " . a:text endfunction function! s:bar(text) call g:mock.called() return "GoodNight, " . a:text endfunction echo s:foo("World") " => Hello, World let g:mock = s:get_mock(expand("%:p")) call g:mock.once().method('foo').will('bar') echo s:foo("World") " => GoodNight, World call g:mock.assert()
sourceして実行してみる。
Hello, World
GoodNight, World
この場合は「一回だけ(once)」という挙動に合うので何も言われない。
mattnさんの記事にある通り処理内容も変わっている。
これを、
let g:mock = s:get_mock(expand("%:p")) call g:mock.once().method('foo').will('bar') echo s:foo("World") echo s:foo("World") echo s:foo("World") call g:mock.assert()
のように複数回呼ぶと「期待している数と違うよ」と怒られる。
Error detected while processing function 499:
line 2:
Function foo is expected 1 bat was 3
「1回って言ったのに3回も呼んでるじゃん!」
- イケてない・気になる部分
- モックオブジェクト作成部分
function! s:get_mock(fname) let mock = { \ 'fname' : a:fname, \ '_old' : '', \ '_new' : '', \ '_called_actual' : 0, \ '_called_expected' : 0, \} function! mock.once() let self._called_expected = 1 return self endfunction function! mock.method(funcname) let self._old = a:funcname return self endfunction function! mock.will(funcname) let self._new = a:funcname " 上書きしちゃうと影響ありそうなので最終的には戻すなどの対応が必要 call s:hook_func(s:get_func(self.fname, self._old), s:get_func(self.fname, self._new)) return self endfunction function! mock.assert() if self._called_actual != self._called_expected echoerr printf('Function %s is expected %s bat was %s', self._old, self._called_expected, self._called_actual) endif endfunction function! mock.called() let self._called_actual += 1 return self endfunction return mock endfunction
- コード全部
" @see http://mattn.kaoriya.net/software/vim/20090826003359.htm function! s:get_sid(fname) let snlist = '' redir => snlist silent! scriptnames redir END let smap = {} let mx = '^\s*\(\d\+\):\s*\(.*\)$' for line in split(snlist, "\n") let smap[tolower(substitute(line, mx, '\2', ''))] = substitute(line, mx, '\1', '') endfor return smap[tolower(a:fname)] endfunction function! s:get_func(fname, funcname) let sid = s:get_sid(a:fname) return function("<SNR>".sid."_".a:funcname) endfunction function! s:hook_func(funcA, funcB) if type(a:funcA) == 2 let funcA = substitute(string(a:funcA), "^function('\\(.*\\)')$", '\1', '') else let funcA = a:funcA endif if type(a:funcB) == 2 let funcB = substitute(string(a:funcB), "^function('\\(.*\\)')$", '\1', '') else let funcB = a:funcB endif let oldfunc = '' redir => oldfunc silent! exec "function ".funcA redir END let g:hoge = oldfunc exec "function! ".funcA."(...)\nreturn call('" . funcB . "', a:000)\nendfunction" endfunction " ------------------ function! s:get_mock(fname) let mock = { \ 'fname' : a:fname, \ '_old' : '', \ '_new' : '', \ '_called_actual' : 0, \ '_called_expected' : 0, \} function! mock.once() let self._called_expected = 1 return self endfunction function! mock.method(funcname) let self._old = a:funcname return self endfunction function! mock.will(funcname) let self._new = a:funcname " 上書きしちゃうと影響ありそうなので最終的には戻すなどの対応が必要 call s:hook_func(s:get_func(self.fname, self._old), s:get_func(self.fname, self._new)) return self endfunction function! mock.assert() if self._called_actual != self._called_expected echoerr printf('Function %s is expected %s bat was %s', self._old, self._called_expected, self._called_actual) endif endfunction function! mock.called() let self._called_actual += 1 return self endfunction return mock endfunction " ------------------ function! s:foo(text) return "Hello, " . a:text endfunction function! s:bar(text) call g:mock.called() return "GoodNight, " . a:text endfunction echo s:foo("World") " => Hello, World let g:mock = s:get_mock(expand("%:p")) call g:mock.once().method('foo').will('bar') echo s:foo("World") " => GoodNight, World call g:mock.assert()