Neovim Plugin Testing with Mocks: Customizing Child Neovim Instances in lazydocker.nvim
Table of Contents
Testing Neovim plugins is tricky: side effects like spawning jobs (lazydocker),
creating windows, notifications, etc.
In lazydocker.nvim tests, we use a mock system to:
- Simulate missing executables (
docker,podman,lazydocker) - Capture
jobstartparams (cmd, env) - Stub window APIs (
nvim_win_is_valid,nvim_open_win) - Intercept
vim.notifyfor error verification
Powered by mini.test child Neovim instances.
Child Neovim: Isolated Sandboxes#
Bootstrap:
local child = helpers.new_child_neovim() -- MiniTest child
child.setup() -- Minimal init: readonly=false, small viewport (15x40)
child.load_lzd(config) -- require('lazydocker').setup(config)
child.unload_lzd() -- Cleanup: package.loaded, globals, augroups
Lifecycle management:
hooks = {
pre_case = child.setup,
post_case = child.unload_lzd,
post_once = child.stop,
}
Mock Factory System#
tests/mocks.lua exports Mocks table with factories:
local Mocks = {}
Mocks.apply = function(mocks) for _, m in ipairs(mocks) do m.apply() end end
Mocks.restore = function(mocks) ... end -- Batch apply/restore
Executable Mocks (simulate PATH):
Mocks.vim_fn_executable_no_docker = function(child)
return create_executable_mock(child, "cmd == 'docker' and 0 or 1")
end
-- Overrides: vim.fn.executable(cmd) → 0 for docker, 1 otherwise
Jobstart Capture:
Mocks.vim_fn_jobstart(child) -- Logs: _G.mock_logs.jobstart = {cmd='lazydocker', env?, on_exit=fn}
-- Returns fake job ID 99
Notify Spy:
Mocks.vim_fn_notify(child) -- _G.notify_messages = [{msg=..., level=ERROR}]
Real Test Examples:#
Missing Docker:
local Mocks = { mocks.vim_fn_executable_no_docker(child), mocks.vim_fn_notify(child) }
mocks.apply(Mocks)
lua("LazyDocker.open({ engine = 'docker' })")
eq(#get('_G.notify_messages'), 1)
eq(get('_G.notify_messages')[1].msg, 'LazyDocker: "docker" command not found...')
mocks.restore(Mocks)
Read other posts