A Cloverage custom reporter plugin that outputs Cobertura XML coverage reports.
Cobertura XML is the de-facto standard consumed by CI/CD systems such as GitLab CI, Jenkins (Cobertura Plugin), GitHub Actions (coverage summary actions), Codecov, and SonarQube.
| Tool | Version |
|---|---|
| Java | 8+ |
| Clojure | 1.10+ |
| Leiningen | 2.x (optional) |
Clojure CLI (clj) |
1.11+ (optional) |
| cloverage / lein-cloverage | 1.2.x |
Add the library to your project's :dependencies (or :dev profile):
;; project.clj
:dependencies [[org.clojars.tooooolong/clojure-cobertura-coverage "0.1.3"]]
:profiles {:dev {:plugins [[lein-cloverage "1.2.4"]]}}Add a :coverage alias to your deps.edn:
;; deps.edn
{:aliases
{:coverage
{:extra-paths ["test"]
:extra-deps {cloverage/cloverage {:mvn/version "1.2.4"}
org.clojars.tooooolong/clojure-cobertura-coverage
{:mvn/version "0.1.3"}}
:main-opts ["-m" "cloverage.coverage"
"--custom-report" "cloverage.coverage.cobertura/report"
"--output" "target/coverage"]}}}lein cloverage --custom-report cloverage.coverage.cobertura/reportThe report is written to target/coverage/cobertura.xml (or whatever
directory you have configured as cloverage's :output).
:cloverage {:output "target/coverage"
:custom-report cloverage.coverage.cobertura/report}Then run the normal coverage command:
lein cloverageWith the alias defined in deps.edn (see Installation above):
clj -M:coverageTo override options on the command line, append them after --:
# Custom output directory
clj -M:coverage --output target/my-coverage
# Also generate the built-in HTML report
clj -M:coverage --htmlThe --custom-report flag is additive — Cloverage still runs all the
reporters you enable (HTML, text, etc.) in addition to the custom one:
# Leiningen
lein cloverage --html --custom-report cloverage.coverage.cobertura/report
# Clojure CLI
clj -M:coverage --htmlThe generated cobertura.xml follows the Cobertura 4 DTD schema:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE coverage SYSTEM "http://cobertura.sourceforge.net/xml/coverage-04.dtd">
<coverage line-rate="0.7500" branch-rate="0.0"
lines-covered="6" lines-valid="8"
branches-covered="0" branches-valid="0"
complexity="0" version="1" timestamp="1712134567">
<sources>
<source>src</source>
</sources>
<packages>
<package name="example" line-rate="0.7500" branch-rate="0.0" complexity="0">
<classes>
<class name="example.core" filename="example/core.clj"
line-rate="0.7500" branch-rate="0.0" complexity="0">
<methods/>
<lines>
<line number="7" hits="3" branch="false"/>
<line number="12" hits="2" branch="false"/>
<line number="18" hits="0" branch="false"/>
...
</lines>
</class>
</classes>
</package>
</packages>
</coverage>| Clojure namespace | Cobertura package | Cobertura class |
|---|---|---|
example.core |
example |
example.core |
example.util.str |
example.util |
example.util.str |
core (top-level) |
(default) |
core |
This repository includes an example namespace (example.core) that
deliberately leaves two functions (divide, factorial) untested so you
can see partial coverage in the report.
# Leiningen
lein cloverage --custom-report cloverage.coverage.cobertura/report
# Clojure CLI (uses the :coverage alias in this repo's deps.edn)
clj -M:coverageAfter the run, inspect target/coverage/cobertura.xml.
test:
script:
# Leiningen
- lein cloverage --custom-report cloverage.coverage.cobertura/report
# — or Clojure CLI —
# - clj -M:coverage
coverage: '/Coverage: (\d+\.\d+)%/'
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: target/coverage/cobertura.xmlstage('Test') {
steps {
// Leiningen
sh 'lein cloverage --custom-report cloverage.coverage.cobertura/report'
// — or Clojure CLI —
// sh 'clj -M:coverage'
}
post {
always {
cobertura coberturaReportFile: 'target/coverage/cobertura.xml'
}
}
}- name: Run tests with coverage (Leiningen)
run: lein cloverage --custom-report cloverage.coverage.cobertura/report
# — or Clojure CLI —
# - name: Run tests with coverage (Clojure CLI)
# run: clj -M:coverage
- name: Upload coverage report
uses: actions/upload-artifact@v4
with:
name: coverage
path: target/coverage/cobertura.xmlCloverage's --custom-report option accepts a fully-qualified symbol
(namespace/function). After running all instrumented tests, Cloverage
calls the function with a single map:
{:output "target/coverage" ; output directory
:forms [...] ; raw coverage forms collection
:args {...} ; parsed CLI options
:project {...}} ; Leiningen project mapThis reporter:
- Calls
cloverage.report/file-statson:formsto get per-file metrics (covered lines, instrumented lines, etc.) - Groups the raw forms by file and calls
cloverage.report/line-statsper file to obtain per-line hit counts - Builds a Cobertura XML document mapping namespaces → packages/classes
- Writes
cobertura.xmlto the output directory
This project uses a GitHub Actions release workflow that
deploys to Clojars automatically when you push a v* tag.
Add this repository secret in Settings → Secrets and variables → Actions:
| Secret | Value |
|---|---|
CLOJARS_DEPLOY_TOKEN |
A Clojars deploy token for the publishing account |
The workflow uses the fixed Clojars username tooooolong; the token replaces the
password when deploying.
# 1. Ensure main is clean and tests pass
git checkout main
git pull
# 2. Tag the release (the workflow strips the leading 'v')
git tag vX.Y.Z
git push origin vX.Y.ZThe workflow will:
- Extract the version from the tag (
vX.Y.Z→X.Y.Z) - Patch
project.cljwith that version in the ephemeral CI workspace - Run
lein deploy clojars
The checked-in project.clj can stay on a placeholder development version.
The release workflow rewrites it in CI from the tag you push, so the published
artifact version always matches the release tag.
Distributed under the Eclipse Public License 2.0.