Vim 8.0 のすゝめ

第2回 単体テスト

2016.12.22

「自動化されたテストはソフトウェアの保守に役立つが、テストを書くのも手間がかかる。いかに楽にテストを書くか、それが問題だ……」Vim 使いの「ブイ」(仮名)です。記念すべき 2 回目のテーマは「単体テスト」です。Vim 8.0 に大量追加された単体テスト関連の関数群。これらは何のために追加されたのか、Vim プラグイン開発での活用方法について解説を行います。

1 レガシーテスト

まずは、なぜ Vim にテスト関数が追加されたのか解説しなければいけません。テスト関数が追加されたのは、本体のレガシーな単体テストを置き換えるためです。

レガシーなテストとはいったいどんなものなのか、例を見てみましょう。みなさん、お手元の Vim ソースコード src/testdir/test1.in と src/testdir/test1.ok を開いてみてください。

https://github.com/vim/vim/blob/14207f487c9e79a913256a41e3e9716b03b46955/src/testdir/test1.in

STARTTEST
:" If columns or lines are too small, create wrongtermsize.
:" (Some tests will fail. When columns and/or lines are small)
:if &lines < 24 || &columns < 80 | sp another | w! wrongtermsize | qa! | endif
:"
:" Write a single line to test.out to check if testing works at all.
:%d
athis is a test:w! test.out
:" Create small.vim and tiny.vim empty, create mbyte.vim to skip the test.
0D:w! small.vim
:w! tiny.vim
ae! test.ok
w! test.out
qa!
:w! mbyte.vim
:w! mzscheme.vim
:w! lua.vim
:"
:" If +multi_byte feature supported, make mbyte.vim empty.
:if has("multi_byte") | sp another | w! mbyte.vim | q | endif
:"
:" If +mzscheme feature supported, make mzscheme.vim empty.
:if has("mzscheme") | sp another | w! mzscheme.vim | q | endif
:"
:" If +lua feature supported, make lua.vim empty.
:if has("lua") | sp another | w! lua.vim | q | endif
:"
:" If +eval feature supported quit here, leaving tiny.vim and small.vim empty.
:" Otherwise write small.vim to skip the test.
:if 1 | q! | endif
:w! small.vim
:" If +windows feature not supported :sp will fail and tiny.vim will be
:" written to skip the test.
:sp another
:wq! tiny.vim
:qa!
ENDTEST

https://github.com/vim/vim/blob/14207f487c9e79a913256a41e3e9716b03b46955/src/testdir/test1.ok

this is a test

はい。呪文のようなものが現れたことでしょう。これは通称レガシーテストと呼ばれるものです。

Vim はレガシーテストの実行時 STARTTEST から ENDTEST を上から順に実行します。: で始まるのが Ex コマンド(Vim script)で、それ以外がキー入力です。実行結果 test.out が test{テスト番号}.ok という結果になればテスト成功という意味になります。しかし見てもらえばわかる通り、このテストは読むのも書くのも実行するのも手間がかかります。そのため、これまではあまり積極的にテストを書かれてきませんでした。

テストというのは、ただでさえ手間がかかるものです。機能を追加したりバグを見つかたら新しいテストを書いたりテストを修正したりする必要があります。テスト形式というのはモチベーションに大きく影響を与えるので、読み書き実行しやすいものでなければいけません。

2 新スタイルテスト

新スタイルテストでは、Vim script を実行することでテストを行います。

具体例を見てみましょう。みなさん、お手元の Vim ソースコード src/testdir/test_arglist.vim を開いてみてください。

https://github.com/vim/vim/blob/14207f487c9e79a913256a41e3e9716b03b46955/src/testdir/test_arglist.vim

func Test_argidx()
  args a b c
  last
  call assert_equal(2, argidx())
  %argdelete
  call assert_equal(0, argidx())

  args a b c
  call assert_equal(0, argidx())
  next
  call assert_equal(1, argidx())
  next
  call assert_equal(2, argidx())
  1argdelete
  call assert_equal(1, argidx())
  1argdelete
  call assert_equal(0, argidx())
  1argdelete
  call assert_equal(0, argidx())
endfunc

皆さんは当然、Vim script は基礎教養として身に付けているでしょうから上記のコードなら問題ありませんね。レガシーテストとは異なり、各テスト関数を実行しアサート関数を用いてチェックを行っているのが分かるかと思います。

新スタイルテストの場合、どこでエラーになったかを表示することができるのでエラーも分かりやすいです。

3 Vim プラグインでの活用

新スタイルテストを書かなければいけないのは、Vim にパッチを書く人達なので、ユーザーには関係のない話なのですが Vim 8.0 で追加されたテスト関数は Vim プラグイン開発でも有用です。

テスト関数の仕様を調べるために、:help assert_equal() を実行してみましょう。

assert_equal({expected}, {actual}, [, {msg}])
                When {expected} and {actual} are not equal an error message is
                added to |v:errors|.
                There is no automatic conversion, the String "4" is different
                from the Number 4.  And the number 4 is different from the
                Float 4.0.  The value of 'ignorecase' is not used here, case
                always matters.
                When {msg} is omitted an error in the form "Expected
                {expected} but got {actual}" is produced.
                Example: >
        assert_equal('foo', 'bar')
<               Will result in a string to be added to |v:errors|:
        test.vim line 12: Expected 'foo' but got 'bar' ~

{excepted}{actual} を比較して、一致していなかったら v:errors にエラーメッセージが代入される、とあります。

では、簡単な例で試してみましょう。

call assert_equal(1, 2)
echo v:errors

上記のスクリプトをファイルに保存して :source % で実行してみてください。「ファイル名:行番号:エラーメッセージ」の形式でエラーが表示されることが分かると思います。

つまりスクリプトにアサート関数を埋め込み、実行後に v:errors の内容をチェックするだけで Vim プラグインのテストができるわけです。

これまでも Vim プラグインには数多くのテストフレームワークが作られました。しかし、テストフレームワークをインストールしなければならないのはなかなかに手間です。そのフレームワークがどれだけサポートされるかという問題もあります。Vim 組み込みのテスト関数は他のテストフレームワークよりもできることは単純ですが、Vim 組み込みで用意されているというのは大きなアドバンテージです。

Vim のテスト関数は assert_equal() 以外にも数多く存在します。ここでそれらを一つずつ紹介しているときりがないので、みなさんは :help functions から使用可能なテスト関数を調べてみてください。

次回は「24bit カラー対応」について解説します。これまでの端末の Vim は 256 色に対応していました。Vim が 24bit カラーに対応すると何が嬉しいのでしょうか? 次回をお楽しみに。

See you the next Vim8.0!

This article is made by Vim.

著者プロフィール

v

ブイ。社内では数少ない Vim 使い。ブログ記事の執筆により、社内でのVim の知名度を上げ、Vim を使用する人を増やそうと計画しているらしい。

記事一覧Index