When building Docker image for a Clojure project, we endup testing outside of the final environment.
Here I try to benefit from Docker layers to build/test from the same Java environment that will be used to run the Docker image.
We can run the test step independently of the build of the complete image ⇒ we can run tests in a dedicated step of our CI pipeline
If we configure our test-runner to output results in a file (XUnit format), we can retrieve this file leveraging Docker volumes when calling "docker run"
➜ docker build --target test --tag clj-example-tests .
➜ docker run clj-example-tests:latest
# ... <SKIPPED>
[(.)]
1 tests, 1 assertions, 0 failures.
Let’s introduce a failing test
➜ cat >> test/exo/add_test.clj<< EOF
(deftest will-fail
(is (= 1 (sum 1 1))))
EOF
➜ docker build --target test --tag clj-example-tests .
Here is output of the run
➜ docker run clj-example-tests:latest
# ... <SKIPPED>
[(F.)]
Randomized with --seed 875721236
FAIL in exo.add-test/will-fail (add_test.clj:11)
Expected:
1
Actual:
-1 +2
2 tests, 2 assertions, 1 failures.
➜ echo $?
1
At any time we can override the CMD to be executed in our test layer to execute different tests
For example:
➜ docker run clj-example-tests:latest clojure -M:test -m kaocha.runner --focus-meta :integration
# ... <SKIPPED>
[(.)]
1 tests, 1 assertions, 0 failures.
Artifact is built in a "build" layer and later copied on a "deployment" layer that does not include all the build tools ⇒ lightweight docker image
➜ docker build --tag clj-example .
# ... <SKIPPED>
Successfully built 880afb3fef23
Successfully tagged clj-example:latest
Then we can execute our program
➜ docker run clj-example:latest 1 2 3
Result: 6
Here are image sizes involved in this pipeline
➜ docker image list
REPOSITORY TAG IMAGE ID CREATED SIZE
clj-example latest 533dd371f7ba 7 seconds ago 342MB
<none> <none> afed52b6047a 8 seconds ago 468MB
<none> <none> ab0222b7d14a 13 seconds ago 463MB
ubuntu focal 2b4cba85892a 43 hours ago 72.8MB
DevContainer configuration is provided in .devcontainer/devcontainer.json
It is reusing test layer ⇒ Clojure and our dependencies are already installed ⇒ jack-in is really fast
That is allowing us to develop in the exact same environment that will be used to validate in the CI or deploy our application