Article 3: Integrating Jackson for YAML Parsing with TDD in a Gradle Plugin
Published on 25 September 2025
Introduction
In Gradle plugin development, managing complex configurations is a common task. Rather than overloading the DSL with hundreds of properties, it is often cleaner and more maintainable to define the configuration in an external file, such as YAML. This article guides you through integrating the powerful Jackson library to parse YAML files into Kotlin objects, adopting a rigorous TDD approach to ensure the robustness of our plugin.site-baker.
1. The Context: Our Plugin `site-baker
Our plugin`site-baker`is designed to automate the generation and deployment of a static site. It needs to read a YAML configuration file (managed-jbake-context.yml) to obtain information such as source paths, deployment destinations, and Git identifiers.
The YAML configuration we want to parse looks like this:
bake:
srcPath: "./site/jbake"
destDirPath: "bake"
cname: "cheroliv.com"
pushPage:
from: "bake"
to: "cvs"
repo:
name: "trainings"
repository: "https://github.com/pages-content/pages-content.github.io.git"
credentials:
username: "USERNAME"
password: "SECRET_TOKEN"
branch: "main"
message: "cheroliv.com"
# ... autres configurations (pushMaquette, supabase, etc.)
2. Modeling of the Configuration in Kotlin
Before parsing, we need to define the structure of our data in Kotlin. Jackson will use these classes to automatically map the YAML content.
// plugin/src/main/kotlin/com/cheroliv/site/baker/data/SiteConfiguration.kt
package com.cheroliv.site.baker.data
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
import com.fasterxml.jackson.module.kotlin.readValue
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
fun parseSiteConfiguration(yaml: String): SiteConfiguration {
val mapper = ObjectMapper(YAMLFactory()).registerKotlinModule()
return mapper.readValue(yaml)
}
data class GitPushConfiguration(
val from: String = "",
val to: String = "",
val repo: RepositoryConfiguration = RepositoryConfiguration(),
val branch: String = "",
val message: String = "",
)
data class RepositoryConfiguration(
val name: String = "",
val repository: String = "",
val credentials: RepositoryCredentials = RepositoryCredentials(),
) {
companion object {
const val ORIGIN = "origin"
const val CNAME = "CNAME"
const val REMOTE = "remote"
}
}
data class RepositoryCredentials(val username: String = "", val password: String = "")
data class SiteConfiguration(
val bake: BakeConfiguration = BakeConfiguration(),
val pushPage: GitPushConfiguration = GitPushConfiguration(),
val pushMaquette: GitPushConfiguration = GitPushConfiguration(),
val pushSource: GitPushConfiguration? = null,
val pushTemplate: GitPushConfiguration? = null,
val supabase: SupabaseContactFormConfig? = null
)
data class BakeConfiguration(
val srcPath: String = "",
val destDirPath: String = "",
val cname: String? = null,
)
// ... (autres data classes pour Supabase, si nécessaire)
The function`parseSiteConfiguration`is our entry point for deserialization. It uses`ObjectMapper`of Jackson, configured with`YAMLFactory`for the YAML format and`registerKotlinModule()`for the support of Kotlin specifics (such as default property values).
3. Integration of Jackson in the `build.gradle.kts
To use Jackson, we must add the necessary dependencies in the`build.gradle.kts`of our module`plugin`</think>
// plugin/build.gradle.kts
dependencies {
// Jackson for YAML parsing
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.18.3")
implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.18.3")
// ... autres dépendances
}
We use`jackson-module-kotlin`for Kotlin support and`jackson-dataformat-yaml`for managing the YAML format.
4. The TDD Approach: Testing YAML Parsing
Now, let’s apply TDD to validate our parsing logic.
4.1. Unit Test: Mapping YAML to Kotlin Object
We start with a unit test in`SiteBakerPluginTest.kt`to check that the function`parseSiteConfiguration`can correctly transform a YAML string into an object`SiteConfiguration`.
// plugin/src/test/kotlin/com/cheroliv/site/baker/SiteBakerPluginTest.kt
@Test
fun `can map configuration text to SiteConfiguration object`() {
val yamlString = "../../managed-jbake-context.yml"
.run(::File)
.readText()
.trimIndent()
val config: SiteConfiguration = parseSiteConfiguration(yamlString)
assertEquals("cheroliv.com", config.bake.cname)
assertEquals("main", config.pushPage.branch)
assertEquals("https://github.com/pages-content/pages-content.github.io.git", config.pushPage.repo.repository)
}
This test reads the content of the file`managed-jbake-context.yml`(which must exist for the test to be valid) and performs assertions on the object`SiteConfiguration`resulting. It validates that the key values of the YAML are correctly mapped.
4.2. Functional Test: Validate Reading the Configuration File
To ensure that the plugin can read the configuration file via its DSL and that parsing works in a real Gradle environment, we add a functional test in`SiteBakerPluginFunctionalTest.kt`.
This test is crucial because it simulates the execution of the plugin in a real project. It must behermetic, that is, he must create the file himself`managed-jbake-context.yml`with a controlled content, to ensure the reproducibility and isolation of the test.
// plugin/src/functionalTest/kotlin/com/cheroliv/site/baker/SiteBakerPluginFunctionalTest.kt
@Test
fun `config file contains good data`(){
val configContent = configFile.readText(UTF_8)
// Vérifications comme dans vos commentaires
assertTrue(configContent.contains("bake"))
assertTrue(configContent.contains("pushPage"))
assertTrue(configContent.contains("repository"))
}
This functional test verifies that the configuration file copied into the test temporary directory contains the expected data. Although this test does not directly parse the YAML into a Kotlin object, it validates the presence of the file and its content, which is an essential prerequisite step for parsing by the plugin.
5. Parsing Flow Visualization
The following diagram illustrates the data flow and interactions when parsing the YAML configuration:
@startuml
actor "User Gradle" as User
participant "build.gradle.kts" as BuildScript
participant "SiteBakerPlugin" as Plugin
participant "Site Extension" as Extension
participant "managed-jbake-context.yml" as ConfigFile
participant "parseSiteConfiguration()" as Parser
participant "SiteConfiguration (Kotlin object)" as KotlinObject
User -> BuildScript : Applique le plugin et configure le DSL
BuildScript -> Plugin : apply()
Plugin -> Extension : Crée et enregistre l'extension 'site'
User -> BuildScript : site { configPath = "..." }
BuildScript -> Extension : Définit configPath
Plugin -> ConfigFile : Lit le fichier (via configPath)
ConfigFile --> Plugin : Contenu YAML
Plugin -> Parser : Appelle parseSiteConfiguration(yamlString)
Parser -> KotlinObject : Mappe le YAML en objet SiteConfiguration
KotlinObject --> Plugin : Retourne l'objet configuré
@enduml
Conclusion
By following a TDD approach, we successfully integrated the Jackson library to parse YAML configuration files into Kotlin objects within our Gradle plugin. This method allowed us to :
-
To model clearlyour configuration with Kotlin data classes
-
Validate the parsing logicwith targeted unit tests.
-
Ensure integrationin a real Gradle environment thanks to hermetic functional tests.
This solid foundation gives us great confidence to extend our plugin with features that build on this configuration, while ensuring the maintainability and robustness of the code. YAML parsing is now a reliable and tested feature of our plugin.site-baker.