elide test runs JavaScript and TypeScript node:test files and JUnit tests.
elide test
A failing test makes the exit code non-zero.
~/demo elide test --coverage
test/greet.test.ts
✓ greets by name
✓ greets the world
╭Coverage Report─────────────────────────────╮
│ TypeScript · 1 file │
│ Statements ━━━━━━━━━━━━━━━━━━━━ 6/6 100% │
│ Lines ━━━━━━━━━━━━━━━━━━━━ 1/1 100% │
│ Functions ━━━━━━━━━━━━━━━━━━━━ 4/4 100% │
╰────────────────────────────────────────────╯
2 pass
0 skip
0 fail
Ran 2 tests across one file. (0.07s)
JavaScript and TypeScript
Tests use the node:test API and need no manifest. A file is a test if it matches *.test.*, *.spec.*,
*_test.* or *_spec.*, or is under test, tests or __tests__. Extensions: .js, .mjs, .cjs, .ts,
.mts, .cts.
Discovery skips hidden, dependency and build-output directories. It stops at any nested project (a directory with
elide.pkl, package.json, pom.xml or requirements.txt), so a workspace root finds no member tests. Name a path
to run them.
import test from "node:test";
import assert from "node:assert";
test("adds two values", () => {
assert.equal(1 + 1, 2);
});
JVM tests
JVM tests run on the JUnit Platform and need an elide.pkl. src/test is the default test source set; a declared
source set of type test replaces it (Source Sets).
- Test sets get
testPackagesplus the main set’s dependencies. - Bundled JUnit libraries are added by default.
jvm.features.testing = falseremoves both the JUnit compile APIs and the bundled runtime; a JVM test run then needs a JUnit console launcher (junit-platform-console) intestPackages, and fails with an error otherwise. Runs that include no JVM tests are unaffected. processorsrun on test sources.- The test JVM gets
jvm.flagsandjvm.defs(as-D). modulesdependencies of the test and main sets go on--module-pathwith--add-modules=ALL-MODULE-PATH.- Parallel settings in a
junit-platform.propertieson the test classpath override--concurrency.
Select tests
Paths, directories or globs narrow JavaScript and TypeScript tests:
elide test src/api
elide test src/api/client.test.ts
Path arguments skip JVM tests and print Path filtering unsupported for JVM tests: <paths>. -t matches test names
in both runners:
elide test -t 'parses .+ headers'
JVM tests take JUnit selectors and tag expressions:
elide test --select=class:com.example.ParserTest
elide test --select=method:com.example.ParserTest#parsesHeaders
elide test --select=package:com.example.parser # the package and its subpackages
elide test com.example.parser # a dotted bare token is a package selector
elide test --include-tag=fast --include-tag=unit # fast OR unit
elide test --exclude-tag=slow --exclude-tag=flaky # neither slow nor flaky
elide build test --select=class:com.example.ParserTest
~/demo elide test --select class:calc.CalculatorTest
calc.CalculatorTest
✓ divides evenly() [13ms]
✓ adds two numbers() [3ms]
✓ rejects division by zero() [19ms]
3 pass
0 skip
0 fail
Ran 3 tests across one file. (0.63s)
~/demo elide test --select 'method:calc.FormatTest#formats cents as dollars'
calc.FormatTest
✓ formats cents as dollars() [23ms]
1 pass
0 skip
0 fail
Ran one test across one file. (0.59s)
--include-tag='slow & !flaky' is one expression. Tag filters apply after selectors.
Output and reports
Progress goes to stderr; the final tally goes to stdout:
elide test > summary.txt
elide test 2> progress.txt
Reporters write to .dev/reports/tests/<language>/<source-set>/. testing.reports in elide.pkl turns them on
without a flag:
import "elide:Testing.pkl" as Testing
testing {
reports { Testing.xmlReport() }
}
TAP output
--reporter=tap makes stdout a pure TAP 13 stream:
TAP version 13
# start 1: suite A > t1
# out 1: printed from inside a test
ok 1 - suite A > t1
# start 2: broken
not ok 2 - broken
---
message: "expected 1 to equal 2"
severity: "ERR_ASSERTION"
...
1..2
# start <id>opens a test; its output lines are# out <id>: <text>.# out: <text>belongs to no test.- Test points are numbered
1..Nin finishing order. Match points to tests by the# startline, not the number. - A YAML block appears only on failures with details;
severityis the runner’s error code, if any. - Stderr is not framed:
elide test --reporter=tap 2>&1is not valid TAP. - JVM: output is tagged with its test only while exactly one test runs. With
--concurrencyabove 1 or a projectjunit-platform.properties, it is untagged. - JavaScript:
console.loginside a test, or at top level after importingnode:test, is framed. Output before that import is raw. Hook output belongs to the test the hook wraps;t.diagnosticis framed as# out.
Set ELIDE_TEST_METADATA=1 with --reporter=tap to include versioned
# elide-test: JSON comments before results. These carry the result number,
start identity, engine, source file, suite, status and measured duration in
milliseconds. Start identity connects concurrent tests to their captured output,
even when names repeat and completion order differs. Ordinary TAP consumers ignore
these comments; default output and per-suite reports remain unchanged.
Coverage
elide test --coverage
elide run --coverage
In a Tern project workspace, choose Test, then Run with coverage. The Coverage tab
appears once the run is measured: it lists the measured files with their covered, partly covered and
uncovered lines, and shows the selected file’s current source with each measured line tinted by its
coverage. Lines the runtime never measures (closing braces, blank lines, comments) take a lighter
tint when the block they belong to ran: the nearest measured line above them in that block was
covered and no uncovered line comes between. They are not counted in the percentages.
Next uncovered and Previous uncovered (n and p) move between gaps, and
Open line N in Elide source opens the file at the line in focus; save your changes and choose
Run again to measure the updated code. Run history keeps previous measurements available, and
unavailable branch data is shown separately from zero coverage.
The open buttons in the Tests view (Open in Elide source, and Open on a test’s card) respond to a click only. A terminal program drawing a Tern view cannot ask Tern to open a link from a key, so Enter does not open the selection; click the button, or ⌘-click a linked file name. See Coverage in Tern.
Test integrations can set ELIDE_COVERAGE_REPORT to a run-specific file path when
requesting coverage. Elide atomically writes a versioned JSON artifact with measured
source files, executable-line coverage and branch counters where available. This
artifact is independent of configured LCOV or JaCoCo report files. An integration
should allocate a new destination for each run.
JavaScript source fingerprints describe the text evaluated by the runtime, even if the file changes before the report is written. Coverage is a measurement of that source version. A missing fingerprint or branch measurement means unavailable data; it does not mean that the current file matches or that no branches executed. Line coverage records whether a line was covered, not how many times it ran.
JVM coverage goes to .dev/reports/coverage/jvm/<task>/ as HTML, XML or CSV; JavaScript and TypeScript coverage is
LCOV, in .dev/reports/coverage/js/lcov.info for tests and .dev/reports/coverage/js/run/lcov.info for runs. The
console prints one combined summary. The top-level coverage block collects without the flag:
import "elide:Coverage.pkl" as Coverage
coverage {
enabled = true
scope = "tests"
jvm {
excludes { "org.example.generated.*" }
reports {
Coverage.xmlReport()
Coverage.htmlReport()
}
minimum {
line = 0.85
branch = 0.70
}
}
js {
paths { "src" }
minimum { line = 0.80 }
}
}
| Property | Meaning | Default |
|---|---|---|
enabled | Collect without --coverage. | false |
scope | "tests", "runs" or "all"; --coverage always covers its own task. | "tests" |
jvm.reports | Coverage.htmlReport(), xmlReport(), csvReport(). | none |
jvm.excludes | Class patterns (*, ?) not instrumented, such as generated code. | none |
jvm.minimum.line, jvm.minimum.branch | Minimum covered ratio, 0.0 to 1.0. | none |
js.paths | Source roots to report; empty means the whole project. | none |
js.minimum.line | Minimum covered line ratio, 0.0 to 1.0. | none |
When a run collects coverage, elide test exits non-zero if a minimum is not met, naming the property, the required
ratio and the measured one. A minimum with nothing to measure, such as no JVM tests or no analyzed classes, also fails.
Reports are written either way. JVM ratios count every class the test run analyzes.
With coverage, elide run starts a separate JVM process.
Options
| Option | Effect |
|---|---|
-t REGEX | Match test or enclosing-suite names. |
--where CEL | JavaScript: run tests for which a CEL expression over name, parents and file is true. |
--select=SELECTOR | JVM: class:, method: or package: selector; repeatable. |
--include-tag=EXPR | JVM: run tests whose tags match; repeats are ORed. |
--exclude-tag=EXPR | JVM: skip tests whose tags match; repeats are ANDed. |
--bail[=N] | Stop after N failures (default 1). |
--test-timeout=MS | Default per-test timeout. |
--timeout DURATION | Time limit for the whole invocation (global). |
--only | Run only tests marked .only. |
--concurrency=N | Run files or JVM classes in parallel. Default 1; 0 is invalid. |
--coverage | Collect coverage; see Coverage. |
--reporter=junit | Write JUnit XML reports. |
--reporter=tap | Write TAP 13 to stdout (and under --reporter-outfile). |
--reporter-outfile=DIR | Report directory. |
--debugger[= | Attach a debugger; see below. |
--debugger-host=HOST, --debugger-port=PORT | JavaScript debugger address (DAP: 4711; CDP: 1337). |
--debugger form | Debugs |
|---|---|
| bare | JVM tests over JDWP on 127.0.0.1:5005, and JavaScript tests. |
=5005, =host:5005 | JVM tests only (JDWP). |
=dap, =cdp | JavaScript tests only. |
Grammar: Debugging. An unknown option or missing value fails before anything runs (Build Tasks).
Limits
- No Python test discovery; run unittest or pytest instead (Python tests).
- JVM tests need a manifest.
- Not implemented:
node:testsnapshots,mock.module, watch mode, programmaticrun(),node:test/reporters.
Supported API: Node test API.
Samples
Start from a template:
elide init --template java # Java app with a JUnit test
elide init --template ktjvm # Kotlin app with a test
Shared test containers
Declare disposable services in testing.testcontainers. Elide starts them before the project’s selected
JavaScript, TypeScript, and JUnit runners, waits for readiness, and stops them when the test run finishes.
Every runner in that project receives the same connection values. This works independently of Micronaut
or any other application framework.
amends "elide:project.pkl"
name = "database-tests"
testing {
testcontainers {
containers {
["postgres"] {
image = "postgres:17-alpine"
environment {
["POSTGRES_USER"] = "test"
["POSTGRES_PASSWORD"] = "test"
["POSTGRES_DB"] = "app"
}
ports { 5432 }
wait { timeoutSeconds = 90 }
}
}
environment {
["DATABASE_HOST"] = "${postgres.host}"
["DATABASE_PORT"] = "${postgres.port.5432}"
["JDBC_URL"] = "jdbc:postgresql://${postgres.host}:${postgres.port.5432}/app"
}
}
}
Read these values through process.env in JavaScript/TypeScript or System.getenv in Java/Kotlin.
Bindings override inherited environment variables for the test runners only. A binding can include
${name.host}, ${name.port.CONTAINER_PORT}, or ${name.id}; unknown references fail before startup.
Ports are TCP ports inside the container. Testcontainers assigns available host ports, so concurrent runs
do not need fixed port reservations. Workspace members have independent container sets.
Each container supports image, an optional command argument list, environment, ports, and wait:
| Wait strategy | Required settings | Behavior |
|---|---|---|
"port" (default) | Nonempty ports | Wait for exposed ports to listen |
"http" | wait.port naming an exposed port | GET wait.path (default /), expecting wait.status (default 200) |
"log" | wait.pattern | Wait for one matching Java regular expression in container logs |
wait.timeoutSeconds defaults to 60. The overall startupTimeoutSeconds defaults to 300 and includes
image downloads and all readiness checks. Startup failures fail the test run. Container command arguments
are passed directly; use an explicit shell command inside the container if needed.
A Docker-compatible daemon and JDK 17 or newer with source-file launching support are required. Elide uses its
configured JVM toolchain and resolves Testcontainers separately from application dependencies on first
use. Docker connection settings such as DOCKER_HOST are inherited. This path is also used by the native
Elide CLI; Testcontainers runs in a helper JVM. It adds nothing to your application JAR or Native Image.
elide install and compilation do not start containers. elide test retains normal test selection,
reporting, and coverage behavior; elide build test supports containers for JVM test tasks too. Cleanup
runs after success, failure, or cancellation. The helper monitors its parent connection, and Testcontainers’
resource reaper handles orphaned Docker resources. Keep the resource reaper enabled in CI.
Session files live under .dev/testcontainers/. Requests containing container environment values are
deleted after the helper reads them and again during cleanup. Helper logs remain there for diagnosis;
they can contain output from the container library. On POSIX systems, session directories are private.
The testcontainers project sample contains JavaScript and JUnit tests against one shared HTTP container.
Run it with elide test.