user=> (* 6 7)
42
Generate Clojure tests from AsciiDoc and CommonMark doc code blocks.
Example code in docs, while likely not malicious, could be illustrating, for example, how to wipe a drive. |
Initial concept. Currently being used to test doc blocks in rewrite-cljc.
Alpha, this might not be a good general idea, but might work well for some projects.
Feedback welcome.
I wanted to make sure the code examples I provided in rewrite-cljc documentation do not mislead and would function as expected for those who dared to try them.
Test-doc-blocks might (or might not, see limitations, and interesting alternatives) be of interest for your Clojure project too.
If your example code blocks are REPL style:
user=> (* 6 7)
42
Or editor style:
(* 6 7)
;; => 42
And don’t rely on any special unspecified setup, then test-doc-blocks might work for your project too.
Add an alias to your deps.edn
:
:gen-doc-tests {:extra-deps {lread/test-doc-blocks {:mvn/version "1.0.101-alpha"}}
:exec-fn lread.test-doc-blocks/gen-tests}
Then the most basic usage is:
clojure -X:gen-doc-tests
This generates Clojure tests for code blocks in your README.md
to the target/test-doc-blocks/test
directory.
Any existing tests under target/test-doc-blocks
will be replaced.
The generated tests have no dependency on test-doc-blocks and can be run with your preferred test runner.
Test-doc-blocks looks for assertions in your doc code blocks that are in editor style and REPL style format. For example…
user=> (/ 714 17)
42
…generates the test assertion (is (= 42 (/ 714 17)))
.
For more detailed examples and inline options, see:
The test-doc-blocks deps.edn has some aliases that might server as useful examples:
:isolated/cljs-test-runner
- runs generated tests under ClojureScript using cljs-test-runner
Invoke for this project via: clj -M:isolated/cljs-test-runner
:isolated/kaocha
- runs generated tests under Clojure using kaocha
Invoke for this project via: clj -M:isolated/kaoacha generated
. Note also kaocha tests.edn config.
:isolated/clj-test-runner
- runs generated tests under Clojure using Cognitect test-runner
Invoke for this project via: clj -M:isolated/clj-test-runner
The default file to generate tests for is README.md.
If you want to specify a different vector of files you can do so via :docs
:
clojure -X:gen-doc-tests :docs '["README.adoc" "doc/example.adoc" "doc/example.md"]'
The default directory to generate tests is ./target
.
You can override this via :target-root
:
clojure -X:gen-doc-tests :target-root '"./someplace/else"'
Note that test-doc-blocks will delete and recreate test-docs-block/test
dirs under the target root.
Keep that the location mind when figuring out where to point your test runner.
The platform governs what Clojure file types will be generated for tests.
Specify:
:clj
for Clojure, generates .clj
files
:cljs
for ClojureScript, generates .cljs
files
:cljc
for mixed, generates .cljc
files
The default is :cljc
.
Platform can be overridden for code blocks via inline options.
Some current limitations that we might entertain addressing:
If your code block depends on some external setup, we’ve no way to express that.
Test-doc-blocks will automatically handle inline (require …)
and (import …)
appearing in code blocks, but not in any complex expressions of these forms.
Parsing adoc and md files is on the naive side but should handle most common cases. If we’ve overlooked a common syntax, let us know.
Some limitations we have no current plans to address:
Code blocks using ns
or in-ns
will not work with test-doc-blocks.
It is possible to embed HTML into your docs. If your code or headings are expressed in embedded HTML within your doc, test-doc-blocks won’t find them.
Here are other options and related projects that I am currently aware of:
kaocha - Koacha supports running cucumber tests.
It uses this support in tests for its own documentation.
A .feature
document describes the feature and includes given, when, then scenarios that are both run and shown in documentation.
Gritty details can be hidden in step definitions.
readme - Generates tests for code blocks found in .md files and then runs them.
testdoc - Tests code blocks in docstrings and external docs.
alc.x-as-tests - Runs code in (comment …)
blocks as tests.
In addition to the Clojure CLI, you will need to install babashka to run scripts.
The example ClojureScript test runner does make use of Node.js
clojure -M:kaocha unit
Run integration test via:
clojure -M:kaocha integration
This will generate tests for README and example docs and then diff against a previously manually verified test run.
The previously verified test run is stored under test-resources/expected
.
On failure careful manual inspection is recommended. When you are happy with current behaviour of generation of tests:
bb script/gen_local_tests.clj regen-expected
Generate tests for local docs via:
bb script/gen_local_tests.clj
Run generated tests under Clojure via:
clojure -M:isolated/kaocha generated
And under ClojureScript via:
clojure -M:isolated/cljs-test-runner
We use clj-kondo to lint project source and fail the build when linting fails.
To run linting as the CI server does:
bb script/lint.clj
rewrite-cljc versioning scheme is: major
.minor
.patch
-test-qualifier
major
increments when the API has been broken - something, as a rule, we’d like to avoid.
minor
increments to convey significant new features have been added.
patch
indicates bug fixes - it is the number of commits in the repo.
test-qualifier
is absent for stable releases. Can be alpha
, beta
, rc1
, etc.
The project maintainer manually manages major
minor
and test-qualifier
in version.edn
.
A release is triggered manually via a GitHub Action "Release" workflow. A release can currently only be cut at main branch HEAD.
GitHub Actions prompts for the branch when running the Release workflow.
I’ve not found a way to disable this, just leave it set to main
.
At this time, the release workflow does not run tests. The assumption is that you’ve waited for the last CI test run to complete and are happy with the results.
Our GitHub Actions "Release" workflow:
Calculates version (commit count will compensate for upcoming release commit) and applies to:
the deps.edn
usage example in this doc
the "unreleased" headings in change log (new "unreleased heading also prepended")
pom.xml
version and scm→tag
a newly built thin jar
Deploys jar to clojars
Commits updated docs and release tag back to repo
Informs cljdoc of the new release
Creates a release tag at HEAD
After a release don’t forget to pull the changes made by the release workflow. |
As a developer you can run the following locally:
bb script/release.clj validate
- to validate your change log is setup appropriately for release
bb script/release.clj prep
- to complete all steps up to, but not including, deploy, note that this will affect changes to README.adoc
, CHANGELOG.adoc
and pom.xml
, you’ll NOT want to commit these changes.
@seancorfield - idea is based on Sean’s readme project.
Helpful feedback and ideas from:
@borkdude
@sogaiu
@dominicin
@pez
@uochan
@lread
Copyright © 2021 Lee Read, all rights reserved.
Distributed under the EPL License, same as Clojure. See LICENSE.
Concept based on @seancorfield’s readme which is distributed under EPL v1.0 or later.
Can you improve this documentation? These fine people already did:
lread & GitHub ActionsEdit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close