transcriptor
Convert REPL interactions into example-based tests.
Using
transcriptor is in the Maven Central repository.
Clojure deps.edn:
com.cognitect/transcriptor {:mvn/version "0.1.5"}
lein project.clj:
[com.cognitect/transcriptor "0.1.5"]
Problem
Testing frameworks often introduce their own abstractions for e.g. evaluation order, data validation, reporting, scope, code reuse, state, and lifecycle. In my experience, these abstractions are always needlessly different from (and inferior to) related abstractions provided by the language itself.
Adapting an already-working REPL interaction to satisfy such testing abstractions is a waste of time, and it throws away the intermediate REPL results that are valuable in diagnosing a problem.
So transcriptor aims to do less, and impose the bare minimum of cognitive load needed to convert a REPL interaction into a test. The entire API is four functions:
xr/run
runs a REPL script and produces a transcriptcheck!
validates the last returned value against a Clojure specxr/on-exit
lets you register cleanup code to run afterxr/run
completesxr/repl-files
finds the.repl
files in a directory tree
Approach
Work at the REPL. Whenever you want to convert a chunk of work into a
test, just copy it into a file with a .repl suffix. You can later call
xr/run
on a REPL file:
(require '[cognitect.transcriptor :as xr :refer (check!)])
(xr/run "your-file-name-here.repl")
run
launches a REPL that consumes all forms in the file passed
in. run
will
- isolate execution in a one-off namespace whose name is printed to stdout. (If the script fails, you can enter this namespace and poke around.)
- pretty print every evaluation result, providing a transcript as if you had repeated the REPL interactions by hand.
Evaluation Order
Clojure language (REPL) semantics.
Validation
Clojure language semantics plus one function.
transcriptor includes a single validation form, check!
, that will
check an argument (by default *1
) against a provided spec, throwing
an exception if the error does not match:
(+ 1 1)
(check! even?)
Exceptions are failures and unwind the stack back to the call to xr/run
.
Reporting
Read clojure.spec error data directly, or pipe it to an error reporter or visualizer of your choice.
Code reuse
Clojure language semantics. Write functions in namespaces and have .repl scripts require them as needed.
Scope
Clojure language semantics. def
vars that you need.
State
Clojure language semantics. (For testing code with nontrivial state I recommend simulation-based testing instead).
Lifecycle
Clojure language semantics plus one function.
The xr/on-exit
function is a no-op outside xr/run
. Inside, it will
queue a function that will be called after the REPL exits.
Test Automation
Clojure language semantics plus one function.
xr/repl-files
returns a seq of .repl files under a directory root, suitable for passing toxr/run
.
Test Repeatability
Clojure language semantics.
Keep Dumb Tests Ugly
Tests that want an exact value match can use a Clojure set as a spec:
(+ 1 2)
(check! #{3}) ;; duh
This is ugly by design, as an inducement to test properties instead of specifics.
License
Eclipse Public License, same as Clojure. https://www.eclipse.org/legal/epl-v10.html
Contributing
Please open a Github issue if your have feedback.