Neotest Scala adapter, supports running tests implemented using various popular Scala test libraries.
This project is a complete overhaul of neotest-scala adapter for neotest started by Stevan Milic.
Supports the following Scala testing libraries:
Runs tests with sbt or Bloop (faster, but support is experimental!).
Requires nvim-metals to get project metadata information
Support levels below describe test execution + result reporting in neotest.
| Library | Test type | Build tool | Support | Notes |
|---|---|---|---|---|
| ScalaTest | AnyFunSuite, AnyFreeSpec, AnyFlatSpec, AnyPropSpec, AnyWordSpec, AnyFunSpec, AnyFeatureSpec, RefSpec (+ Async* / Fixture* variants where applicable) |
sbt |
Full | Stable path via JUnit XML reports. |
| ScalaTest | AnyFunSuite, AnyFreeSpec, AnyFlatSpec, AnyPropSpec, AnyWordSpec, AnyFunSpec, AnyFeatureSpec, RefSpec (+ Async* / Fixture* variants where applicable) |
bloop |
Limited | Uses stdout parsing for results (with additional JUnit report flags passed to runner); matching is best-effort vs XML. |
| munit | FunSuite, CatsEffectSuite, ScalaCheckSuite, DisciplineSuite, ZSuite, ZIOSuite |
sbt |
Full | Stable path via JUnit XML reports. |
| munit | FunSuite, CatsEffectSuite, ScalaCheckSuite, DisciplineSuite, ZSuite, ZIOSuite |
bloop |
Limited | Uses stdout parsing; single-test runs are executed at suite scope for reliability, and No test suites were run is reported as failure. |
| specs2 | Specification (>>, in, basic ! fragments) |
sbt |
Limited | General execution works, but single-test selection can still run a larger scope/spec. |
| specs2 | Specification (>>, in, basic ! fragments) |
bloop |
Limited | Uses stdout parsing; supports fail/crash markers, but matching remains best-effort. |
| specs2 | text spec (s2""" ... """) |
sbt |
Limited | Execution works, but fine-grained single-test runs are limited. |
| specs2 | text spec (s2""" ... """) |
bloop |
Limited | Same single-test limits plus stdout parsing constraints. |
| zio-test | ZIOSpecDefault |
sbt |
Full | Stable path via JUnit XML reports. |
| zio-test | ZIOSpecDefault |
bloop |
Not supported | Automatically forced to sbt (bloop execution is disabled for this framework). |
| uTest | TestSuite |
sbt |
Full | Works for run/result flow; interpolated names may run at suite scope with numeric JUnit result mapping; DAP nearest-test runs at file scope. |
| uTest | TestSuite |
bloop |
Not supported | Known issue: uTest suites can't be discovered by bloop; tests will be run by sbt. |
Recommendation: prefer
sbtfor stability. Usebloopwhen speed matters and current framework limitations are acceptable.
Using lazy.nvim:
{
'nvim-neotest/neotest',
dependencies = {
'nvim-neotest/neotest-plenary',
'olisikh/neotest-scala'
},
},require("neotest").setup({
adapters = {
require("neotest-scala")
}
})By default (build_tool = "auto"), the plugin follows build tool information from Metals build target metadata to stay in sync with your editor session.
Selection priority in auto mode:
- If Metals metadata clearly indicates
sbt, usesbt(recommended default for stability) - If Metals metadata clearly indicates
bloop, usebloop - If metadata is ambiguous, fall back to local detection (
.bloop/+bloopexecutable), otherwisesbt
Framework compatibility guard:
- If selected tool is
bloopbut the detected test library does not support bloop execution, neotest-scala automatically runs that test withsbt.
You can explicitly configure the build tool:
require("neotest").setup({
adapters = {
require("neotest-scala")({
build_tool = "bloop", -- "auto" (default), "bloop", or "sbt"
})
}
})Why Bloop? Bloop is significantly faster than sbt because:
- It keeps a running compilation server (no JVM startup overhead)
- It caches compilations incrementally
- It's already running if you use Metals
You may override some arguments that are passed into the build tool when you are running tests:
require("neotest").setup({
adapters = {
require("neotest-scala")({
args = { "--no-colors" },
})
}
})Also you have an option to dynamically specify args for SBT:
require("neotest").setup({
adapters = {
require("neotest-scala")({
args = function(args)
-- args table contains:
-- path - full path to the file where test is executed
-- framework - test library name that your test is implemented with
-- project_name - project name the test is running on
-- build_target_info - information about the build target collected from Metals
return { "-v" }
end,
})
}
})neotest-scala writes logs to /tmp/neotest-scala/log/<YYYY-MM-DD>.log.
Each line includes a logger name (for example adapter, results, metals) so you can see which module emitted it.
require("neotest").setup({
adapters = {
require("neotest-scala")({
logging = {
enabled = true, -- default: true
level = "info", -- debug | info | warn | error
},
})
}
})build_spec payload logging uses INFO level.
The plugin provides diagnostic information for failing tests directly in your editor:
- Error messages from test failures
- Stack trace line numbers
- Inline error indicators via Neotest
Diagnostics are automatically displayed when tests fail, making it easy to identify and fix issues without leaving Neovim.
Plugin also supports debugging tests with nvim-dap (requires nvim-metals).
debug nearest test intentionally launches file-scope debug for reliability.
Per-test DAP selectors are not supported.
Runs that report No test suites were run. are marked as failures (including DAP runs).
See wiki/Debugging.md and wiki/Troubleshooting.md for limitations and troubleshooting details.
To run tests with debugger pass strategy = "dap" when running neotest:
require('neotest').run.run({strategy = 'dap'})