Generating test reports
The MUnit sbt plugin supports collecting historical data about your test reports and displaying HTML report summaries about your test suites. The test report data is stored in Google Cloud Storage and the HTML report is rendered with an MDoc markdown modifier.
Example
Below is the generated report from the tests in the MUnit codebase.
D: Average duration in milliseconds, P: Passed, E: Failed, F: Flaky, R: Flaky/Passed ratio, S: Skipped, I: Ignored, O: Other
Name | D | P | E | F | R | S | I | O |
---|---|---|---|---|---|---|---|---|
munit.AssertionsSuite.basic | 32 | 1517 | 0 | 0 | 0.0 | 318 | 0 | 0 |
munit.AssertionsSuite.expr | 0 | 1517 | 0 | 0 | 0.0 | 318 | 0 | 0 |
munit.AssertionsSuite.subexpr | 2 | 1517 | 0 | 0 | 0.0 | 318 | 0 | 0 |
munit.AssertionsSuite.subtype | 1 | 959 | 0 | 0 | 0.0 | 250 | 0 | 0 |
munit.AsyncFixtureOrderSuite.teardown runs only after test completes | 54 | 481 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.AsyncJSSuite.async-error | 0 | 456 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.AsyncJSSuite.async-ok | 0 | 456 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.ClueSuite.clue | 0 | 1517 | 0 | 0 | 0.0 | 318 | 0 | 0 |
munit.ClueSuite.clues | 0 | 1517 | 0 | 0 | 0.0 | 318 | 0 | 0 |
munit.ClueSuite.comment | 0 | 1835 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.ClueSuite.identifier | 4 | 1835 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.ClueSuite.lambda | 0 | 1499 | 18 | 0 | 0.009809264305177113 | 318 | 0 | 0 |
munit.ClueSuite.list | 0 | 1517 | 0 | 0 | 0.0 | 318 | 0 | 0 |
munit.ClueSuite.product | 0 | 691 | 0 | 0 | 0.0 | 815 | 424 | 0 |
munit.ClueSuite.select | 0 | 1835 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.ClueSuite.string-message | 0 | 1517 | 0 | 0 | 0.0 | 318 | 0 | 0 |
munit.DiffsSuite.ansi | 6 | 1835 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.DiffsSuite.trailing-whitespace | 0 | 1835 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.DiffsSuite.windows-crlf | 0 | 1835 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.FrameworkSuite.AssertionsFrameworkSuite | 6 | 89 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.FrameworkSuite.AsyncFixtureFrameworkSuite | 21 | 174 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.FrameworkSuite.AsyncFixtureTeardownFrameworkSuite | 3 | 174 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.FrameworkSuite.CiOnlyFrameworkSuite | 12 | 459 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.FrameworkSuite.DiffProductFrameworkSuite | 12 | 459 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.FrameworkSuite.DuplicateNameFrameworkSuite | 3 | 89 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.FrameworkSuite.FailFrameworkSuite | 4 | 459 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.FrameworkSuite.FailSuiteFrameworkSuite | 4 | 325 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.FrameworkSuite.FixtureFrameworkSuite | 6 | 459 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.FrameworkSuite.InterceptFrameworkSuite | 9 | 379 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.FrameworkSuite.ScalaCheckExceptionFrameworkSuite | 6 | 50 | 0 | 0 | 0.0 | 0 | 34 | 0 |
munit.FrameworkSuite.ScalaCheckFrameworkSuite | 187 | 289 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.FrameworkSuite.ScalaVersionFrameworkSuite | 1 | 459 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.FrameworkSuite.StackTraceFrameworkSuite | 4 | 53 | 0 | 0 | 0.0 | 0 | 36 | 0 |
munit.FrameworkSuite.StackTraceFrameworkSuite-1 | 1 | 53 | 0 | 0 | 0.0 | 0 | 36 | 0 |
munit.FrameworkSuite.SuiteTransformCrashFrameworkSuite | 3 | 315 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.FrameworkSuite.SuiteTransformFrameworkSuite | 3 | 315 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.FrameworkSuite.SwallowedExceptionSuite | 7 | 374 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.FrameworkSuite.TagsFrameworkSuite | 2 | 459 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.FrameworkSuite.TagsFrameworkSuite-1 | 1 | 459 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.FrameworkSuite.TagsFrameworkSuite-2 | 1 | 459 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.FrameworkSuite.TestNameFrameworkSuite | 3 | 459 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.FrameworkSuite.TestTransformCrashFrameworkSuite | 5 | 315 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.FrameworkSuite.TestTransformFrameworkSuite | 1 | 315 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.FrameworkSuite.ValueTransformCrashFrameworkSuite | 4 | 315 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.FrameworkSuite.ValueTransformFrameworkSuite | 8 | 315 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.FunFixtureSuite.basic | 1 | 1835 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.FutureSuite.nested | 27 | 959 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.LazyFutureSuite.nested | 1 | 1221 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.LazyFutureSuite.ok-task | 0 | 1482 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.LinesSuite.basic | 5 | 1835 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.LinesSuite.multiline | 0 | 1835 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.PrintersSuite.array | 1 | 1835 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.PrintersSuite.basic | 1 | 1835 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.PrintersSuite.list | 0 | 1835 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.PrintersSuite.map | 2 | 1835 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.PrintersSuite.multiline | 0 | 1835 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.PrintersSuite.newline | 0 | 1835 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.PrintersSuite.single-quote | 0 | 1835 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.PrintersSuite.user1 | 1 | 1835 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.PrintersSuite.user2 | 0 | 1835 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.ScalaCheckSeedSuite.generated int are not all the same | 1 | 413 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.ScalaCheckSeedSuite.generating int | 72 | 413 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.SuiteLocalFixtureSuite.1 | 0 | 1835 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.SuiteLocalFixtureSuite.2 | 0 | 1835 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.SuiteLocalFixtureSuite.3 | 0 | 1835 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.SuiteLocalFixtureSuite.4 | 0 | 1835 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.SuiteLocalFixtureSuite.5 | 0 | 1835 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.TestLocalFixtureSuite.basic | 1 | 1835 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.TestLocalFixtureSuite.two | 0 | 1835 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.TimeoutSuite.fast | 3 | 1175 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.TimeoutSuite.slow | 103 | 1175 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.TypeCheckSuite.not a member | 4 | 1364 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.TypeCheckSuite.parse error | 0 | 1364 | 0 | 0 | 0.0 | 0 | 0 | 0 |
munit.TypeCheckSuite.type mismatch | 0 | 1364 | 0 | 0 | 0.0 | 0 | 0 | 0 |
Installation
These installation instructions are experimental and subject to change.
First, install the sbt-munit plugin.
// project/plugins.sbt
addSbtPlugin("org.scalameta" % "sbt-munit" % "0.7.12")
Next, setup up Google Cloud authentication credentials. You should have a JSON file that looks like this.
{
"type": "service_account",
"project_id": "...",
"private_key": "---BEGIN PRIVATE KEY---...."
// ...
}
Next, add the following two secret environment variables to your CI settings.
GOOGLE_APPLICATION_CREDENTIALS_JSON
: the base64 encoded value of the JSON file containing the credentials.
export GCP_CREDENTIALS="/path/to/json/file/on/your/computer"
# macOS
cat $GCP_CREDENTIALS | base64 | pbcopy
# Ubuntu (assuming GNU base64)
cat $GCP_CREDENTIALS | base64 -w0 | xclip
# Arch
cat $GCP_CREDENTIALS | base64 | sed -z 's;\n;;g' | xclip -selection clipboard -i
# FreeBSD (assuming BSD base64)
cat $GCP_CREDENTIALS | base64 | xclip
GOOGLE_APPLICATION_CREDENTIALS
: an arbitrary relative filename like"gcp.json"
. This should not match the path on your personal computer. The exact value of this variable doesn't matter as long as the file path is relative and the file does not exists in your repository. The MUnit sbt plugin will generate this file from the base64 secret variable.
Verify that your CI provider only exposes secret environment variables to the jobs that run on master branch and not pull requests.
If you use GitHub actions, open the following URL for your repository https://github.com/scalameta/munit/settings/secrets to configure secret environment variables. Once the secrets are correctly configured, the page should looks something like this:
Merge some changes into master and verify that the test reports are getting uploaded to Google Cloud.
❯ gsutil ls -r 'gs://munit-test-reports/**'
gs://munit-test-reports/scalameta/munit/2020-01-26/refs/heads/master/670f475b49a44a87d515ec48b9749ec196336f78/testsJVM/0.21.0-RC1/1.8.0_232.json
...
Next, setup MDoc to generate documentation. Once you have MDoc setup, enable MUnitReportPlugin
in the same project where MdocPlugin
is enabled.
// build.sbt
lazy val docs = project
.in(file("myproject-docs"))
- .enablePlugins(MdocPlugin)
+ .enablePlugins(MdocPlugin, MUnitReportPlugin)
.settings(...)
Next, you should be able to use the mdoc:munit
modifier to generate test reports from the stored data in Google Cloud.
```scala mdoc:munit
```