neovim/neovim
Testing
Neovim has three test suites and they all live under test/. The full developer-facing description is test/README.md and runtime/doc/dev_test.txt; this page is the cheat sheet.
The three suites
| Suite | Path | Speed | What it does |
|---|---|---|---|
| Unit | test/unit/ |
Fast | Calls C functions directly via FFI. Best for marktree, path, typval encoder, etc. |
| Functional | test/functional/ |
Medium | Spawns a real nvim --embed child and drives it over msgpack-rpc. Most editor behavior tests live here. |
| Old (vim) | test/old/ |
Slow | Vim's original test framework, ported. Used mainly for vim-patch: porting. |
There is also a benchmark suite (test/functional/benchmark/, run with make benchmark) for performance regressions.
The Lua suites are written against busted. The runner is test/runner.lua and the harness — the helpers that spawn the child editor and wrap RPC — is test/testutil.lua (25k lines) and 48k lines).test/harness.lua (
Running
make test # everything
make functionaltest
make unittest
make oldtest
# Run a single file
TEST_FILE=test/functional/lua/diagnostic_spec.lua make functionaltest
# Filter by test name (busted --filter)
TEST_FILTER='vim.diagnostic.set' make functionaltest
# Run with valgrind
VALGRIND=1 TEST_FILE=... make functionaltest
# Run the build under ASAN/UBSAN
rm -rf build
CMAKE_EXTRA_FLAGS="-DCMAKE_C_COMPILER=clang -DENABLE_ASAN_UBSAN=1" make
ASAN_OPTIONS=log_path=/tmp/nvim_asan make functionaltestCI also runs make lint, the docs lint, the windows test matrix, and the vim-patch lint. Locally you usually only need the suite that covers the area you touched.
Anatomy of a functional test
local n = require('test.functional.testnvim')()
local clear, eq, exec_lua = n.clear, t.eq, n.exec_lua
describe('vim.diagnostic.set()', function()
before_each(function()
clear()
end)
it('sets diagnostics for a buffer', function()
exec_lua([[
local ns = vim.api.nvim_create_namespace('test')
vim.diagnostic.set(ns, 0, {
{ lnum = 0, col = 0, message = 'oops' },
})
]])
eq(1, exec_lua('return #vim.diagnostic.get(0)'))
end)
end)Common helpers from test/functional/testutil.lua:
n.clear()— start a fresh nvim child.n.exec_lua(code, ...)— run Lua in the child and return the result.n.api.nvim_…— call API functions on the child via msgpack.n.feed('iHello<esc>')— send keystrokes throughnvim_input.n.command(':wq')— run an Ex command vianvim_command.n.expect(...)andt.eq(...)— assertions.n.screen(Screenintest/functional/ui/screen.lua) — a virtual screen that snapshots redraw events. Used by every UI/redraw test.
Anatomy of a unit test
Unit tests use FFI. They are most useful for testing C functions whose behavior is hard to provoke through the editor:
local helpers = require('test.unit.helpers')
local cimport = helpers.cimport
local marktree = cimport('./src/nvim/marktree.h')
describe('marktree', function()
it('inserts and queries', function()
local mt = ffi.new('MarkTree[1]')
marktree.marktree_put(mt, ...)
-- ...
end)
end)Look at test/unit/marktree_spec.lua for a fully worked example.
What to test
The maintainer expectation is that almost every PR adds tests. Specifically:
- Bug fix. Add a regression test that fails before your change and passes after. Name the test with the issue number, e.g.
it('outputs the EOF as LF (not CRLF) #36853'…). The number lets reviewers check the fix corresponds to the bug. - New feature. Add functional tests for the new behavior plus unit tests for any new pure-data structures.
- Refactor. No new tests required, but existing tests must continue to pass.
Special test environments
oldtest/— the upstream Vim test files, run with the legacyruntest.vimdriver. You will mostly touch this when working onvim-patch:ports.benchmark/— micro-benchmarks. Run withmake benchmark. Failures here are usually performance regressions, not correctness ones.Screensnapshots — UI tests use a virtual screen that snapshots a 2D grid of cells. The snapshot is asserted withscreen:expect{grid=[[...]]}. When you change rendering, you'll be updating these.
Logs
When a functional test fails, the child editor's log goes to $NVIM_LOG_FILE (set by the test harness). The default location is build/.nvimlog or per-test temp dirs. :edit $NVIM_LOG_FILE in a paused test environment is a quick way to see what the child saw.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.