scry is a Clojure test runner for AI agents and REPL-driven development. It returns structured, inspectable test results so callers can identify failures, assertions, stack traces, and captured output without scraping terminal text.
It supports two complementary workflows:
.scry-results/.AI coding agents often need to determine:
scry exposes those answers as Clojure data. Broad runs stay compact, while focused namespace and var runs include progressively more detail.
scry is an initial public alpha / pre-1.0 project. The core clojure.test runner, in-process API, CLI, scoped result model, and optional Kaocha adapter are usable and tested. APIs and result shapes may still change before a stable release.
The project leans heavily on AI-generated code and AI review processes.
Add scry as a test or development dependency. A conventional deps.edn setup is:
{:aliases
{:test
{:extra-paths ["test"]
:extra-deps
{org.hugoduncan/scry {:mvn/version "RELEASE"}}}}}
If your project already has a :test alias, merge the dependency into that alias rather than replacing its paths or options. Adjust :extra-paths if your tests live somewhere other than test.
The examples use Clojars' "RELEASE" token for convenience. For reproducible builds, replace it with the latest concrete version shown on Clojars and keep that version pinned.
Start a REPL on your test alias:
clojure -M:test
Run one test namespace and retain the result:
(require '[scry.core :as scry])
(def result
(scry/run {:namespaces ['my.project-test]}))
Inspect the outcome and canonical result entries:
(select-keys result [:pass? :summary])
(:results result)
(scry/failures result)
scry also retains the most recent result for follow-up inspection:
(scry/last-result)
(println (scry/report-string))
A single explicit namespace includes every executed var and its assertion details. To capture stdout and stderr as well, run one explicit test var:
(require '[my.project-test])
(def var-result
(scry/run {:vars [#'my.project-test/specific-test]}))
(-> var-result :results first (select-keys [:out :err]))
For a failed or erroring var, scry/output retrieves the same captured streams by fully qualified symbol:
(scry/output 'my.project-test/specific-test)
You now have a focused result that can be queried as Clojure data instead of parsed from terminal output.
clojure.test tests in the REPLUse explicit namespaces or vars during development (after requiring the target namespace as shown above):
(scry/run {:namespaces ['my.project-test]})
(scry/run {:vars [#'my.project-test/specific-test]})
Use a directory or namespace pattern for broader discovery:
(scry/run)
(scry/run {:dirs ["test"]})
(scry/run {:ns-pattern #".*-test$"})
Normal clojure.test fixtures retain their standard grouping and ordering behavior. Nested in-process test runs are isolated from the enclosing scry result.
Run all discovered tests with either CLI entry point:
clojure -M:test -m scry.cli
clojure -X:test scry.cli/run
Target a namespace or test var:
clojure -M:test -m scry.cli --namespace my.project-test
clojure -M:test -m scry.cli --var my.project-test/specific-test
clojure -X:test scry.cli/run :namespaces '[my.project-test]'
clojure -X:test scry.cli/run :vars '[my.project-test/specific-test]'
The CLI prints live per-var progress and a summary. At the start of each run it clears and recreates .scry-results/ in the current working directory. When a concrete var completes with a failure or error, its detailed artifact is synchronously written before the next var starts. Publication writes a same-directory temporary file and atomically moves it to the final name, so consumers must inspect only final .edn names and ignore temporary files. Failed and erroring vars produce namespace-prefixed EDN files such as:
.scry-results/my.project-test__specific-test.edn
These files contain assertion details, error stack traces, and captured output. Passing vars do not create artifacts, so passing runs can leave the directory empty. If a callback-time publication fails, the CLI records a bounded diagnostic and retries after a normal runner return; synthetic suite/load failures are also written during that final reconciliation. A published artifact is retained if a later catchable runner error aborts the run, and its final path remains in :result-files. This improves availability after later hangs or aborts, but cannot guarantee an artifact for an incomplete var, a process killed during that file's write window, power loss, or a filesystem publication failure.
The CLI exits 0 only when at least one concrete test var runs and all tests pass. Structured outcomes expose the authoritative :scry.cli/outcome-kind; the -X entry point returns the outcome map on success and throws ex-info with structured outcome data on non-zero results. When artifact publication remains unresolved, :scry.cli/diagnostic-error distinguishes :incremental-result-file-writing (runner threw before returning) from :final-result-file-reconciliation (after a normal runner return). Inspect the outcome and final .scry-results/*.edn files rather than parsing progress or diagnostic text.
See the scry.cli/run reference for outcome kinds, result-file behavior, and error data. Run clojure -M:test -m scry.cli --help for the supported main-style options.
Kaocha support is packaged separately so the core artifact does not depend on Kaocha. Add the adapter under a composable alias:
{:aliases
{:test
{:extra-paths ["test"]
:extra-deps
{org.hugoduncan/scry {:mvn/version "RELEASE"}}}
:kaocha
{:extra-deps
{org.hugoduncan/scry-kaocha {:mvn/version "RELEASE"}}}}}
Use the same version for org.hugoduncan/scry and org.hugoduncan/scry-kaocha. The adapter brings its own Kaocha dependency; projects can override that dependency through normal deps.edn resolution.
Compose the :test and :kaocha aliases, then run configured suites from the command line:
clojure -M:test:kaocha -m scry.cli --runner kaocha unit
clojure -M:test:kaocha -m scry.cli --runner kaocha unit integration
clojure -X:test:kaocha scry.cli/run :runner :kaocha :suite :unit
Main-style Kaocha mode forwards Kaocha options and positional suite selectors to Kaocha's CLI parser:
clojure -M:test:kaocha -m scry.cli --runner kaocha --focus my.ns/test-foo
clojure -M:test:kaocha -m scry.cli --runner kaocha --no-randomize unit
Run Kaocha in-process from a REPL with scry.kaocha:
(require '[scry.kaocha :as kaocha])
(kaocha/run) ;; loads tests.edn when present
(kaocha/run {:suite :unit})
(kaocha/run {:suites [:unit :integration]})
(kaocha/run {:config full-kaocha-config})
Without an explicit :config, the adapter loads the current project's tests.edn when present. Otherwise it builds a synthetic :unit suite from source paths, test paths, and namespace patterns. Kaocha results use the same scoped result model; captured stdout and stderr are currently merged into :out.
See the scry.kaocha/run reference for all adapter options and suite-selection rules. Use clojure -M:test:kaocha -m scry.cli --runner kaocha --help for Kaocha CLI options.
By default, scry/run returns:
{:summary ...
:pass? ...
:results ... ;; canonical formatted entries
:failures ...} ;; compatibility failing/erroring subset
Default detail depends on how narrowly the run is targeted:
| Invocation | Default result detail |
|---|---|
| Discovery, multiple namespaces, or multiple vars | Compact failing/erroring entries |
| One explicit namespace | Every executed var and all assertion details |
| One explicit executable var | One var, all assertion details, and captured :out / :err |
Use :result-format to override the returned keys and inclusions for each scope. See the scry.core/run reference for the complete result shape, scope rules, and formatting options.
scry.core API — in-process runner and inspection helpersscry.cli/run API — structured CLI outcomes and -X behaviorscry.kaocha API — optional Kaocha adapterclojure -M:test -m scry.cli --help — core CLI optionsclojure -M:test:kaocha -m scry.cli --runner kaocha --help — Kaocha CLI optionsscry is licensed under the Eclipse Public License 2.0 (EPL-2.0). See LICENSE.
If you want a change, please open an issue rather than a pull request.
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |