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.
Coverage
elide test --coverage
elide run --coverage
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()
}
}
js {
paths { "src" }
}
}
| 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 |
js.paths | Source roots to report; empty means the whole project. | none |
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. For this repository’s Docker-backed integration checks, run
make test-testcontainers JVM=yes after a JVM build, or make test-testcontainers against a native build.