Lua製Neovim pluginのテストをLuaで書く
NeovimのLua製pluginを実際に動かしてテストするためのコマンドを作った。
CIで動かすためのGitHub Actionも作ったので、なぜ、どんなものを作ったのかをまとめる。
Luaでテストを書く
Vim scriptで書かれたpluginのテストにはthemis.vimが大変便利だけど、
Luaで書かれたpluginのテストはLuaで書きたくなる。
themis.vimから:lua
コマンドでの実行も可能そうだが、Neovim本体のリポジトリで使ってるbustedを使うと同じような書き方ができてよさそう。
例えば以下のようにタブを増やすだけのコマンドがあったなら、そのテスト対象のコマンドを実行して結果をassertしたい。
command! MyPluginCommand tabedit
describe("MyPluginCommand", function()
it("can open a tab", function()
vim.api.nvim_command("MyPluginCommand")
assert.equals(2, vim.fn.tabpagenr("$"))
end)
end)
しかし、vimオブジェクトはNeovim内部のLuaのstate初期化時にグローバル(_G
)にセットされている。
bustedで直接テストを実行してもvimオブジェクトは存在しない。以下のように怒られる。
$ busted
✱
0 successes / 0 failures / 1 error / 0 pending : 0.000399 seconds
Error → spec/example_spec.lua @ 2
MyPluginCommand can open a tab
spec/example_spec.lua:3: attempt to index global 'vim' (a nil value)
Neovim本体ではビルドされたmoduleをbustedで読み込んで実行しているが、各pluginでビルドの面倒をみるのは大変。
じゃあnvimコマンドを経由してbustedを実行するのが楽そう。
ということで、bustedをnvim --headless
でwrapしたコマンドvustedを作って使っている。
luarocks install vusted
でinstall可能。以下のように問題なく実行できる。
$ vusted
ok 1 - MyPluginCommand can open a tab
1..1
テスト上でvimオブジェクトを使える以外はbustedとだいたい同じように使える。
コマンドライン引数もそのまま。--shuffle
とかあって便利。
しかし、nvim --headless
で実行してるのでvimのメッセージがテスト結果と一緒に出力されてしまう。
例えば検索時のsearch hit BOTTOM, continuing at TOP
や、echo
コマンドの出力など。
outputHandlerもTAPに固定していて色付きの格好いい出力はされないが、十分機能はするのであまり問題視していない。
(他のoutputHandlerそのままだとエスケープシーケンスが文字として出ちゃう)
ちなみに、nvim-lua/plenary.nvimにほぼ同じようなmoduleが用意してあって、
そちらではbustedをbundleしている。luaunitも使えるみたい。
vustedを作ったときに存在を知らなかったが、コマンドとして独立していて実行時に何も考えずに使えるものが欲しかったのでヨシ。
CIで動かす
LuaJIT, LuaRocksのセットアップをするためaction-setup-nvim-luaも作った。
(既存のものがWindows未対応だったのと、デフォルトでNeovimに合わせたセットアップを維持するものが欲しかった)
これでGitHub Actionsで楽にテストできるようになった。
Neovim自体のセットアップはaction-setup-vimにお任せしている。とても便利。
jobs:
test:
name: Test
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v2
- uses: notomo/action-setup-nvim-lua@v1
- run: luarocks install vusted
- uses: rhysd/action-setup-vim@v1
id: vim
with:
neovim: true
version: nightly
- name: Run tests
env:
VUSTED_NVIM: ${{ steps.vim.outputs.executable }}
run: make test
Discussion