Recipes
Debugging
To debug tests, make sure you depend on mfussenegger/nvim-dap and rcarriga/nvim-dap-ui.
Then you have two options:
- DAP configuration provided by leoluz/nvim-dap-go (recommended)
- Use your own custom DAP configuration (no additional dependency needed)
Adapter-provided (recommended)
return {
+ {
+ "rcarriga/nvim-dap-ui",
+ dependencies = {
+ "nvim-neotest/nvim-nio",
+ "mfussenegger/nvim-dap",
+ },
+ },
+
{
"nvim-neotest/neotest",
dependencies = {
"nvim-neotest/nvim-nio",
"nvim-lua/plenary.nvim",
"antoinemadec/FixCursorHold.nvim",
"nvim-treesitter/nvim-treesitter",
- "fredrikaverpil/neotest-golang", -- Installation
+ {
+ "fredrikaverpil/neotest-golang", -- Installation
+ dependencies = {
+ "leoluz/nvim-dap-go",
+ },
+ },
},
config = function()
require("neotest").setup({
adapters = {
require("neotest-golang"), -- Registration
},
})
end,
},
}
Use your own custom DAP configuration
return {
+ {
+ "rcarriga/nvim-dap-ui",
+ dependencies = {
+ "nvim-neotest/nvim-nio",
+ "mfussenegger/nvim-dap",
+ },
+ },
+
{
"nvim-neotest/neotest",
dependencies = {
"nvim-neotest/nvim-nio",
"nvim-lua/plenary.nvim",
"antoinemadec/FixCursorHold.nvim",
"nvim-treesitter/nvim-treesitter",
"fredrikaverpil/neotest-golang", -- Installation
},
config = function()
+ local options = {
+ dap_mode = "manual",
+ dap_manual_config = {
+ name = "Debug go tests",
+ type = "go", -- Preconfigured DAP adapter name
+ request = "launch",
+ mode = "test",
+ },
+ }
require("neotest").setup({
adapters = {
+ require("neotest-golang")(options) -- Registration
},
})
end,
},
}
Finally, set keymaps to run Neotest commands.
Keymap for debugging nearest test
Coverage
You can use andythigpen/nvim-coverage to show coverage in Neovim.
Coverage
return {
{
"nvim-neotest/neotest",
dependencies = {
"nvim-neotest/nvim-nio",
"nvim-lua/plenary.nvim",
"antoinemadec/FixCursorHold.nvim",
"nvim-treesitter/nvim-treesitter",
{
"fredrikaverpil/neotest-golang",
version = "*",
dependencies = {
"andythigpen/nvim-coverage", -- Added dependency
},
},
},
config = function()
local neotest_golang_opts = { -- Specify configuration
runner = "go",
go_test_args = {
"-v",
"-race",
"-count=1",
"-coverprofile=" .. vim.fn.getcwd() .. "/coverage.out",
},
}
require("neotest").setup({
adapters = {
require("neotest-golang")(neotest_golang_opts), -- Registration
},
})
end,
},
}
Custom test arguments
You can pass custom arguments, such as build tags, into the adapter either by supplying a configuration or as keymap/command.
Using configuration
If you need to set build tags (like e.g. -tags debug
or -tags "tag1 tag2"
),
you need to provide these arguments both in the go_test_args
and
go_list_args
adapter options. If you want to be able to debug, you also need
to set dap_go_opts
. Full example:
Build tags
return {
{
"nvim-neotest/neotest",
config = function()
require("neotest").setup({
adapters = {
require("neotest-golang")({
go_test_args = { "-count=1", "-tags=integration" },
go_list_args = { "-tags=integration" },
dap_go_opts = {
delve = {
build_flags = { "-tags=integration" },
},
},
}),
},
})
end,
},
}
Using keymap/command
One can override go_test_args
by passing in the extra_args.go_test_args
table. Using this approach, you can set up multiple keymaps or run tests with
different arguments without restarting Neovim.
For instance, if you want to run go test
with the
-v -race -count=1 -p=1 -parallel=10 -tags=integration
flags, you could call:
Extra args
Limited support
Currently, overriding go list
or DAP arguments via extra_args
is not
currently supported but could easily be implemented
in a similar way,
if needed.
Custom environment variables
You can also pass in custom environment variables to the adapter, which will be set when running tests. This can be useful for setting up test-specific environment variables, such as database connection strings or API keys.
Using configuration
You can pass in environment variables by providing a table of key-value pairs
to the env
option in the adapter configuration.
Custom env variables
Using keymap/command
You can also pass in environment variables via the extra_args.env
table when
running tests. This allows you to set environment variables dynamically at
runtime, without needing to restart Neovim.
Custom env variables via extra_args
Pass arguments as function instead of table
Some use cases may require you to pass in dynamically generated arguments during runtime. To cater for this, you can provide arguments as a function.
Args passed as functions
return {
{
"nvim-neotest/neotest",
config = function()
require("neotest").setup({
adapters = {
require("neotest-golang")({
go_test_args = function()
-- provide custom logic here..
return { "-count=1", "-tags=integration" }
end,
go_list_args = function()
-- provide custom logic here..
return { "-tags=integration" }
end,
dap_go_opts = function()
-- provide custom logic here..
return {
delve = {
build_flags = { "-tags=integration" },
},
}
end,
},
}),
},
})
end,
},
}
Per-project configuration
Depending on how you have Neovim setup, you can define the neotest-golang config
on a per-project basis by placing a .lazy.lua
with overrides in the project.
This requires the lazy.nvim plugin
manager.
Example configuration: extra everything
In the below code block, I've provided a pretty hefty configuration example, which includes the required setup for testing and debugging along with all the keymaps. This is a merged snapshot of my own config, which I hope you can draw inspiration from. To view my current config, which is divided up into several files, see:
Extra everything
return {
-- Neotest setup
{
"nvim-neotest/neotest",
event = "VeryLazy",
dependencies = {
"nvim-neotest/nvim-nio",
"nvim-lua/plenary.nvim",
"antoinemadec/FixCursorHold.nvim",
"nvim-treesitter/nvim-treesitter",
"nvim-neotest/neotest-plenary",
"nvim-neotest/neotest-vim-test",
{
"fredrikaverpil/neotest-golang",
dependencies = {
{
"leoluz/nvim-dap-go",
opts = {},
},
},
branch = "main",
},
},
opts = function(_, opts)
opts.adapters = opts.adapters or {}
opts.adapters["neotest-golang"] = {
go_test_args = {
"-v",
"-race",
"-coverprofile=" .. vim.fn.getcwd() .. "/coverage.out",
},
}
end,
config = function(_, opts)
if opts.adapters then
local adapters = {}
for name, config in pairs(opts.adapters or {}) do
if type(name) == "number" then
if type(config) == "string" then
config = require(config)
end
adapters[#adapters + 1] = config
elseif config ~= false then
local adapter = require(name)
if type(config) == "table" and not vim.tbl_isempty(config) then
local meta = getmetatable(adapter)
if adapter.setup then
adapter.setup(config)
elseif adapter.adapter then
adapter.adapter(config)
adapter = adapter.adapter
elseif meta and meta.__call then
adapter(config)
else
error("Adapter " .. name .. " does not support setup")
end
end
adapters[#adapters + 1] = adapter
end
end
opts.adapters = adapters
end
require("neotest").setup(opts)
end,
keys = {
{ "<leader>ta", function() require("neotest").run.attach() end, desc = "[t]est [a]ttach" },
{ "<leader>tf", function() require("neotest").run.run(vim.fn.expand("%")) end, desc = "[t]est run [f]ile" },
{ "<leader>tA", function() require("neotest").run.run(vim.uv.cwd()) end, desc = "[t]est [A]ll files" },
{ "<leader>tS", function() require("neotest").run.run({ suite = true }) end, desc = "[t]est [S]uite" },
{ "<leader>tn", function() require("neotest").run.run() end, desc = "[t]est [n]earest" },
{ "<leader>tl", function() require("neotest").run.run_last() end, desc = "[t]est [l]ast" },
{ "<leader>ts", function() require("neotest").summary.toggle() end, desc = "[t]est [s]ummary" },
{ "<leader>to", function() require("neotest").output.open({ enter = true, auto_close = true }) end, desc = "[t]est [o]utput" },
{ "<leader>tO", function() require("neotest").output_panel.toggle() end, desc = "[t]est [O]utput panel" },
{ "<leader>tt", function() require("neotest").run.stop() end, desc = "[t]est [t]erminate" },
{ "<leader>td", function() require("neotest").run.run({ suite = false, strategy = "dap" }) end, desc = "Debug nearest test" },
{ "<leader>tD", function() require("neotest").run.run({ vim.fn.expand("%"), strategy = "dap" }) end, desc = "Debug current file" },
},
},
-- DAP setup
{
"mfussenegger/nvim-dap",
event = "VeryLazy",
keys = {
{"<leader>db", function() require("dap").toggle_breakpoint() end, desc = "toggle [d]ebug [b]reakpoint" },
{"<leader>dB", function() require("dap").set_breakpoint(vim.fn.input("Breakpoint condition: ")) end, desc = "[d]ebug [B]reakpoint"},
{"<leader>dc", function() require("dap").continue() end, desc = "[d]ebug [c]ontinue (start here)" },
{"<leader>dC", function() require("dap").run_to_cursor() end, desc = "[d]ebug [C]ursor" },
{"<leader>dg", function() require("dap").goto_() end, desc = "[d]ebug [g]o to line" },
{"<leader>do", function() require("dap").step_over() end, desc = "[d]ebug step [o]ver" },
{"<leader>dO", function() require("dap").step_out() end, desc = "[d]ebug step [O]ut" },
{"<leader>di", function() require("dap").step_into() end, desc = "[d]ebug [i]nto" },
{"<leader>dj", function() require("dap").down() end, desc = "[d]ebug [j]ump down" },
{"<leader>dk", function() require("dap").up() end, desc = "[d]ebug [k]ump up" },
{"<leader>dl", function() require("dap").run_last() end, desc = "[d]ebug [l]ast" },
{"<leader>dp", function() require("dap").pause() end, desc = "[d]ebug [p]ause" },
{"<leader>dr", function() require("dap").repl.toggle() end, desc = "[d]ebug [r]epl" },
{"<leader>dR", function() require("dap").clear_breakpoints() end, desc = "[d]ebug [R]emove breakpoints" },
{"<leader>ds", function() require("dap").session() end, desc ="[d]ebug [s]ession" },
{"<leader>dt", function() require("dap").terminate() end, desc = "[d]ebug [t]erminate" },
{"<leader>dw", function() require("dap.ui.widgets").hover() end, desc = "[d]ebug [w]idgets" },
},
},
-- DAP UI setup
{
"rcarriga/nvim-dap-ui",
event = "VeryLazy",
dependencies = {
"nvim-neotest/nvim-nio",
"mfussenegger/nvim-dap",
},
opts = {},
config = function(_, opts)
-- setup dap config by VsCode launch.json file
-- require("dap.ext.vscode").load_launchjs()
local dap = require("dap")
local dapui = require("dapui")
dapui.setup(opts)
dap.listeners.after.event_initialized["dapui_config"] = function()
dapui.open({})
end
dap.listeners.before.event_terminated["dapui_config"] = function()
dapui.close({})
end
dap.listeners.before.event_exited["dapui_config"] = function()
dapui.close({})
end
end,
keys = {
{ "<leader>du", function() require("dapui").toggle({}) end, desc = "[d]ap [u]i" },
{ "<leader>de", function() require("dapui").eval() end, desc = "[d]ap [e]val" },
},
},
{
"theHamsta/nvim-dap-virtual-text",
opts = {},
},
}