For information on ADRs (Architecture Decision Records) see Documenting Architecture Decisions by Michael Nygard.
Status: ACCEPTED
cljs.test
really consists of two parts: a testing library which you use to
define your tests (deftest
, testing
, is
, are
, async
, use-fixtures
),
and a test runner (run-tests
, run-all-tests
, report
).
We want to replace the latter (the test runner) to provide a richer, more interactive user experience, through a browser based UI which allows running all or a subset of tests, and which attractively presents the test results, done in a way that interacts nicely with auto-reload workflows like shadow-cljs/figwheel.
The test library and test runner part of cljs.test
are not clearly separated,
the latter makes some fairly specific assumptions about the former, and vice
versa. We want to stay compatible with existing tests, so we’ll have to work
with the assumptions that the testing library makes, even if that will sometimes
lead to awkward workarounds.
Status: ACCEPTED
cljs.test
is heavily macro-based. The run-*
functions are actually macros
which inspect the compiler state, and based on that generate code that executes
tests. This limits the user experience that tooling is able to provide, because
it is not possible to invoke specific tests dynamically, at runtime.
We use a single macro to extract information about tests from the compiler env (namespaces, fixtures, and test vars), and store that information for later use.
By splitting the data extraction from test running we are decomplecting the
cljs.test
test runner. The downside of this approach is that we are only able
to find information about namespaces that have been analyzed (compiled) by the
time the macro gets executed. In other word, there has to be a top level
namespace which has dependencies (i.e. :require
statements) onto all test
namespaces.
This can be handled automatically by something like the shadow-cljs
:browser-test
target, or through a compiler hook (in environments that have
that), or explicitly/manually by maintaining a list of `:require` statements.
Status: ACCEPTED
We are building Chui with a very specific use case in mind, but at the same time we want to make sure that what we are building is maximally useful to the open source community, now and in the future.
Chui is split into three layers, which can be used together or separately. The bottom layer is the test-data layer described in ADR-002. It allows capturing test data for later use, and may provide some affordances for querying that data once it is captured.
The runner part is the heart of Chui. It allows invoking a test run for a specific subset of tests, to be notified of progress, to interrupt an active test run, and to inspect test results.
Finally we provide a browser-based user interface for interacting with Chui. Running tests, and inspecting results.
It is possible to only use the test-data layer, or only the test-data and runner layers. This allows for the creation of other interfaces like a command line interface, or a websocket interface.
Status: ACCEPTED
JavaScript and hence ClojureScript forms a single-threaded, evented environment. To chain multiple asynchronous events you need some kind of continuation system. Traditionally this was done with callbacks. Modern JavaScript has standardized on the use of promise objects (thenables).
cljs.test
contains its own continuation-passing-style callback-based “async
object”, tagged by the IAsyncTest
protocol.
Where possible we use JS Promises to model asynchrony. To process a chain of potentially asynchronous operations we use a custom implementation of the interceptor pattern.
Promises provide a convenient mental model, and allow us to leverage existing browser-based tooling and libraries. Interceptors provide a convenient way to model, reason about, and execute a multi-stage process.
When running tests or fixtures we need to test for IAsyncTest
return values,
and convert these to promises. The use of interceptors provides us a way to
thread process state through multiple stages by way of the context.
Status: ACCEPTED / SUPERSEDED BY XXX