Skip to content

guidewire-oss/fern-junit-gradle-plugin

Repository files navigation

Fern JUnit Gradle Plugin

A Gradle plugin and CLI for publishing JUnit test results to a Fern test reporting instance.

example workflow plugin

Overview

This plugin simplifies the process of collecting JUnit XML test reports and publishing them to a Fern test reporting service. It parses JUnit XML reports, converts them to Fern's data model, and sends them to your Fern instance through its API.

To learn more about Fern, check out its repository

Features

  • Parse JUnit XML test reports in various formats
  • Group test suites and test cases into a cohesive test run
  • Track test execution time and status
  • Support for test tags
  • Configurable verbosity for debugging

Installation

Add the plugin to your build.gradle.kts file:

plugins {
  id("io.github.guidewire-oss.fern-publisher") version "1.0.0"
}

Or in build.gradle:

plugins {
  id 'io.github.guidewire-oss.fern-publisher' version '1.0.0'
}

Configuration

Register your application with fern

Newer versions of the Fern Reporter server require you to pre-register your application to receive a UUID (your project id)

  1. To register your project send a POST request to <yourFernUrl>/api/project with JSON body of
{
  "name": "my-project",
  "team_name": "Dev Team",
  "comment": "Initial project registration"
}

Here is an example curl command for ease of use:

curl -X POST "https://yourFernUrl.com/api/project" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "my-project",
    "team_name": "Dev Team",
    "comment": "Initial project registration"
  }'
  1. You will receive a successful response that looks like:
{
  "uuid": "59e06cf8-f390-5093-af2e-3685be593a25",
  "name": "my-project",
  "team_name": "Dev Team",
  "comment": "Initial project registration",
  "created_at": "2025-06-06T15:00:28.403029Z",
  "updated_at": "2025-06-06T15:00:28.403682Z"
}

You will need to take note of the returned ProjectID UUID for use in configuring the plugin, as described below

Plugin Setup

Configure the plugin in your build script:

fernPublisher {
  fernUrl.set("https://your-fern-instance.example.com")
  projectId.set("6ba7b812-9dad-11d1-80b4-00c04fd430c8")
  projectName.set("my-project")
  reportPaths.set(listOf("build/test-results/test/*.xml"))
  fernTags.set(listOf("automated", "integration"))
  verbose.set(false)
  failOnError.set(false)
}

Parameters

Property Description Required Default
fernUrl URL of your Fern instance Yes -
projectId The id of your project supplied by registering a project in Fern. No* ""
projectName Name of your project in Fern No* ""
* NOTE: you must include a projectName or a projectID (or both). Newer versions of the Fern Reporting server require you to pre-register your project to obtain an ID
reportPaths Glob patterns for locating JUnit XML reports Yes -
fernTags Tags to apply to all tests No empty list
verbose Enable verbose logging No false
failOnError When true, will fail when error is thrown. Errors will always log. No false

Authentication (Optional)

If your Fern instance requires OAuth authentication, you can configure it using environment variables:

export AUTH_URL="https://auth.example.com/token"
export FERN_AUTH_CLIENT_ID="your-client-id"
export FERN_AUTH_CLIENT_SECRET="your-client-secret"
export FERN_CLIENT_SCOPE="fern.write fernproject.myproject"
Variable Description Required
AUTH_URL OAuth token endpoint URL. Setting this enables OAuth. Yes (to enable auth)
FERN_AUTH_CLIENT_ID OAuth client identifier Yes (if AUTH_URL is set)
FERN_AUTH_CLIENT_SECRET OAuth client secret Yes (if AUTH_URL is set)
FERN_CLIENT_SCOPE Space-separated OAuth scopes (e.g., fern.write fern.read) No

OAuth is enabled when AUTH_URL is set; if it is set, FERN_AUTH_CLIENT_ID and FERN_AUTH_CLIENT_SECRET are required and the plugin will fail fast if either is missing. The plugin uses the OAuth 2.0 client credentials flow and adds the bearer token to API calls.

Alternatively, you can configure OAuth in your build.gradle via the fernPublisher extension. Prefer sourcing secrets from the environment rather than hardcoding them:

fernPublisher {
    authUrl          = System.getenv("AUTH_URL")
    authClientId     = System.getenv("FERN_AUTH_CLIENT_ID")
    authClientSecret = System.getenv("FERN_AUTH_CLIENT_SECRET")
    authScopes       = "fern.write fernproject.myproject"
}

If authUrl is set in the extension it takes precedence; the environment variables above are used as a fallback when authUrl is left blank.

Usage

Run the task to publish test results:

./gradlew publishToFern

You can also make it run automatically after your tests:

tasks.named("test") {
  finalizedBy("publishToFern")
}

Examples

Basic Configuration

fernPublisher {
  fernUrl.set("https://fern.example.com")
  projectName.set("backend-api")
  reportPaths.set(listOf("build/test-results/test/*.xml"))
}

Multiple Report Sources

fernPublisher {
  fernUrl.set("https://fern.example.com")
  projectName.set("full-stack")
  reportPaths.set(
    listOf(
      "${project.buildDir}/test-results/test/*.xml",
      "frontend/build/test-results/test/*.xml"
    )
  )
  fernTags.set(listOf("ci", "nightly"))
}

Compatibility

  • Gradle 6.1 or later
  • Kotlin 1.4 or later
  • JUnit 4 and JUnit 5 XML report formats

Command-Line Interface (CLI)

This project also provides a CLI tool, fern-junit-client, for collecting and publishing JUnit XML test reports to a Fern Reporter instance. It is designed to work the same way as the Gradle plugin, but can be used independently of Gradle and Java.

Builds of the CLI binary are available for Linux, macOS, and Windows on the Releases page.

Usage

fern-junit-client send \
  --fern-url <FERN_URL> \
  --project-name <PROJECT_NAME> \
  --project-id <PROJECT_ID> \
  --file-pattern <REPORT_PATTERN> \
  [--tags <TAGS>] \
  [--verbose]

Options

  • --fern-url (-u): Base URL of the Fern Reporter instance to send test reports to (required)
  • --project-name (-n): Name of the project to associate test reports with (required)
  • --project-id (-i): Project ID to associate test reports with (required)
  • --file-pattern (-f): Glob pattern(s) for JUnit XML reports (can be repeated, required)
  • --tags (-t): (Optional) Comma-separated tags to include on the test run
  • --verbose (-v): (Optional) Enable verbose output for debugging

Example

fern-junit-client send \
  --fern-url https://fern.example.com \
  --project-name my-service \
  --project-id 1234 \
  --file-pattern "build/test-results/**/*.xml" \
  --tags "ci,nightly" \
  --verbose

Building from Source

git clone https://github.com/your-org/fern-junit-publisher.git
cd fern-junit-publisher
./gradlew build

Development

Publishing to Local Maven Repository

To test the plugin locally during development, you can publish it to your local Maven repository (.m2).

Important: You must specify a version number when publishing. Use the latest released version or the next version you plan to release.

./gradlew publishToMavenLocal -Pversion=1.1.0

The plugin will be published to your local .m2 repository at:

~/.m2/repository/io/github/guidewire-oss/fern-junit-gradle-plugin/1.1.0/

Using the Local Plugin in a Test Project

To use your locally published plugin in a test project:

  1. Configure your test project's settings.gradle (or settings.gradle.kts) to use mavenLocal():
pluginManagement {
    repositories {
        mavenLocal()  // Check local repository first
        mavenCentral()
    }
}
  1. Apply the plugin in your test project's build.gradle:
plugins {
    id 'io.github.guidewire-oss.fern-publisher' version '1.1.0'
}
  1. Configure and test the plugin as described in the Configuration section.

Building the CLI

Native CLI binaries are built using GraalVM's native-image tool. This allows the CLI to run without requiring a Java runtime, making it lightweight and portable.

For building the CLI, you will need to have GraalVM installed and set the environment variable GRAALVM_HOME as your GraalVM install location.

Once that is set up, run the following command in the project root:

./gradlew nativeImage -Pversion="1.1.0"

Testing

This project uses JUnit 5 (Jupiter) for testing along with AssertJ for assertions, WireMock for HTTP mocking, and Gradle TestKit for plugin testing.

Running Tests

Run all tests:

./gradlew test

Run tests with verbose output:

./gradlew test --info

Run a specific test class:

./gradlew test --tests TestParserTest
./gradlew test --tests DataSenderTest
./gradlew test --tests FernPublisherPluginTest

Run a specific test method:

./gradlew test --tests "TestParserTest.parseReports should handle empty file pattern"

Continuous testing (watches for changes):

./gradlew test --continuous

Clean and test:

./gradlew clean test

Test Structure

The project includes three main test suites located in src/test/kotlin/:

  • TestParserTest.kt - Tests JUnit XML parsing logic, including:

    • Parsing various JUnit XML formats
    • Handling empty file patterns
    • Timezone handling
    • Test suite and test case extraction
  • DataSenderTest.kt - Tests HTTP client and API communication, including:

    • Sending test data to Fern Reporter API
    • HTTP retry logic
    • Error handling and redirects
  • FernPublisherPluginTest.kt - Tests Gradle plugin functionality, including:

    • Plugin configuration
    • Task registration
    • Integration with Gradle build lifecycle

Test Dependencies

  • JUnit Jupiter (JUnit 5) - Testing framework
  • AssertJ - Fluent assertion library
  • WireMock - HTTP service mocking
  • Gradle TestKit - Gradle plugin testing framework

Packages

 
 
 

Contributors

Languages