Vim - vimtestにモック機能を追加した(vmock連携)

先日作成したこちらのモックライブラリ。

Vim - Vim script用のモックライブラリvmockを作った

これをvimtest側でも使えるようにしました。

コード例

上記記事の例をvimtestで書きなおしてみます。
webapi-vimをモックに置き換えるという例です。

プロダクトコード

function! SampleReadFeed(url)
  let feeds = []
  for item in webapi#feed#parseURL(a:url)

    " some processing...

    call add(feeds, item)
  endfor
  return feeds
endfunction

SampleReadFeedは内部で webapi#feed#parseURL を呼んでおり、
そのままではテスト実行時に通信が発生してしまいます。

モックを使わないテストコード(通信発生しちゃう)

let s:t = vimtest#new('Sample')

function! s:make_item(title, link)
  return {'title': a:title, 'link': a:link}
endfunction

function! s:t.test()
  " expected data
  let items = [s:make_item('Title-1', 'http://localhost/hoge'),
            \ s:make_item('Title-2', 'http://localhost/piyo'),
            \ s:make_item('Title-3', 'http://localhost/fuga')]
  let url = 'http://rss.slashdot.org/Slashdot/slashdot'

  call self.assert.equals(items, SampleReadFeed(url))
endfunction

実際に通信が発生するため、このままではもちろん失敗します。

Sample
 [ ] test


# Sample (in 19-2057.vim)
 1) 'test' is FAILED
  Failed asserting that two values are equal
   - expected:[{'link': 'http://localhost/hoge', 'title': 'Title-1'}, {'link': 'http://localhost/piyo', 'title': 'Title-2'}, {'link': 'http://localhost/fuga', 'title': 'Title-3'}]
   +   actual:[{'id': 'http://slashdot.feedsportal.com/...(長いので省略)]

Test cases run: 1, Assertions: 1, Passes: 0, Failures: 1

モックを使ったテストコード(通信発生しない)

vmockの呼び出しを追加します。

let s:t = vimtest#new('Sample')

function! s:make_item(title, link)
  return {'title': a:title, 'link': a:link}
endfunction

function! s:t.test()
  " expected data
  let items = [s:make_item('Title-1', 'http://localhost/hoge'),
            \ s:make_item('Title-2', 'http://localhost/piyo'),
            \ s:make_item('Title-3', 'http://localhost/fuga')]
  let url = 'http://rss.slashdot.org/Slashdot/slashdot'

  call vmock#mock('webapi#feed#parseURL').with(url).return(items).once()

  call self.assert.equals(items, SampleReadFeed(url))
endfunction
Sample
 [x] test


Test cases run: 1, Assertions: 1, Passes: 1, Failures: 0

通りました。

失敗するケースも見てましょう

期待する引数と実際の引数が違った

実際の呼び出し時の引数を適当にしてみます。

  call self.assert.equals(items, SampleReadFeed('invalid'))
Sample
 [ ] test


# Sample (in 19-2057.vim)
 1) 'test' is FAILED
  Exception:VMock:The args[0] expected: 'http://rss.slashdot.org/Slashdot/slashdot'. but received: 'invalid'.
   in function vimtest#run..466..vimtest#util#safety_call..vmock#verify..vmock#verify_with_event..498..vmock#exception#throw, line 1

Test cases run: 1, Assertions: 2, Passes: 1, Failures: 1

期待する呼び出し回数ではなかった

1度だけ(once)指定になっていますが、わざと2度呼んでみます。

  call self.assert.equals(items, SampleReadFeed(url))
  call self.assert.equals(items, SampleReadFeed(url))
Sample
 [ ] test


# Sample (in 19-2057.vim)
 1) 'test' is FAILED
  Exception:VMock:expected: only once. but received: 2 times.
   in function vimtest#run..518..vimtest#util#safety_call..vmock#verify..vmock#verify_with_event..550..vmock#exception#throw, line 1

Test cases run: 1, Assertions: 3, Passes: 2, Failures: 1

vmock単体で使う場合と記述上で異なるのは、vmock#verifyvmock#clear を呼ばなくて済むことです。

おわり

モックの細かい使い方は上記記事かvmockのヘルプを参照してください。