Articolo 2 : Sviluppare un Plugin Gradle con un Approccio TDD
Publié le 24 September 2025
In questo articolo, esploreremo come impostare una base di sviluppo solida per un plugin Gradle utilizzando un approccio di sviluppo guidato dai test (TDD - Test-Driven Development). Questo metodo garantisce che il nostro codice sia robusto, mantenibile e risponda precisamente ai requisiti fin dall’inizio.
L’inizializzazione del progetto
Gradle facilita notevolmente la creazione di un nuovo plugin grazie al comando`gradle init`. Scegliendo di creare un "Gradle Plugin" con Kotlin, otteniamo una struttura di progetto pronta all’uso, includendo due tipi di test essenziali :
-
Test Unitari :Situati in`src/test`, consentono di validare componenti isolati del nostro plugin, come la logica interna di un’attività o la configurazione di un’estensione. Sono rapidi e non richiedono l’esecuzione completa di Gradle.
-
Test funzionali:Situati in`src/functionalTest`, usano`GradleRunner`per eseguire una build Gradle completa in un progetto di test temporaneo. Questo ci permette di verificare il comportamento reale del plugin in un ambiente controllato.
2. Il nostro primo ciclo TDD: Registrare un compito
La nostra prima richiesta è semplice: il plugin deve registrare un’attività chiamata`printSiteConfig`.
2.1. Il Test Prima (Il Test che Fallisce)
Conformemente al TDD, scriviamo innanzitutto un test che verifica l’esistenza di questa attività. Dentro`SiteBakerPluginTest.kt`(il nostro file di test unitario), aggiungiamo :
@Test
fun `plugin registers task`() {
// Créer un projet de test en mémoire
val project = ProjectBuilder.builder().build()
project.plugins.apply("com.cheroliv.site-baker")
// Vérifier que la tâche a bien été enregistrée
assertNotNull(project.tasks.findByName("printSiteConfig"))
}
Questo test fallisce perché non abbiamo ancora scritto alcun codice nel nostro plugin.
2.2. Il Codice Poi (Far Passare il Test)
Ora scriviamo il minimo di codice necessario nel`SiteBakerPlugin.kt`affinché il test passi :
class SiteBakerPlugin: Plugin<Project> {
override fun apply(project: Project) {
// Enregistrer une tâche simple
project.tasks.register("printSiteConfig") { task ->
// ... la logique de la tâche viendra plus tard
}
}
}
Rilanciamo i test, e passano. La nostra prima funzionalità è validata.
3. Secondo Ciclo TDD: Aggiungere un’estensione DSL
Il requisito successivo è quello di permettere agli utenti di configurare il nostro plugin tramite un blocco DSL nel loro`build.gradle.kts`. Vogliamo un blocco`site { … }`dove si può specificare un percorso di configurazione.
3.1. Il test iniziale
Aggiungiamo un test per verificare che l’estensione`site`è ben registrata e si può assegnarle un valore
@Test
fun `plugin registers extension`() {
val project = ProjectBuilder.builder().build()
project.plugins.apply("com.cheroliv.site-baker")
// Récupérer l'extension et lui affecter une valeur
project.extensions
.findByType(SiteExtension::class.java)!!
.configPath
.set("config.yml")
// Vérifier que la valeur a bien été prise en compte
assertEquals(
"config.yml",
project.extensions.findByType(SiteExtension::class.java)?.configPath?.get()
)
}
Questo test fallisce perché né la classe`SiteExtension`né la registrazione dell’estensione esiste
3.2. Il codice successivo
Stiamo creando la classe`SiteBakerExtension.kt`e aggiorniamo`SiteBakerPlugin.kt` :
// SiteBakerExtension.kt
open class SiteExtension @Inject constructor(objects: ObjectFactory) {
val configPath: Property<String> = objects.property(String::class.java)
}
// SiteBakerPlugin.kt
class SiteBakerPlugin: Plugin<Project> {
override fun apply(project: Project) {
// Enregistrer l'extension
val extension = project.extensions.create("site", SiteExtension::class.java)
project.tasks.register("printSiteConfig") { task ->
task.doLast {
// On utilisera l'extension plus tard
}
}
}
}
I test passano di nuovo.
4. Il Test Funzionale : Validare l’integrazione
Ora che le unità sono testate, dobbiamo assicurarci che tutto funzioni insieme in una vera build. Questo è il ruolo del test funzionale in`SiteBakerPluginFunctionalTest.kt`.
4.1. Il Test di Integrazione: Creare un Ambiente Controllato
Questo test simulerà un vero progetto che utilizza il nostro plugin. Per essere affidabile, deve essereermetico, ovvero non deve dipendere da file esistenti sul sistema. Deve creare da sé tutte le condizioni necessarie alla sua esecuzione.
Il test quindi :
-
Creare un`build.gradle.kts`di test che utilizza il nostro plugin e il suo DSL.
-
Creare il file di configurazione(
managed-jbake-context.yml) che il plugin dovrebbe leggere. È un passaggio cruciale per la robustezza del test. -
Eseguire il compito`printSiteConfig`via`GradleRunner`.
-
Verificare che l’output della build sia corretto.
Copiando o creando questo file di configurazione ad ogni esecuzione, ci assicuriamo che il test siariproducibilee non dipende da uno stato esterno. Questo rende i nostri test sicuri contro le regressioni che potrebbero essere legate alla lettura del file.
@Test fun `can run task with DSL`() {
// 1. Créer un build.gradle.kts de test
buildFile.writeText("""
plugins { id("com.cheroliv.site-baker") }
site { configPath = "managed-jbake-context.yml" }
""".trimIndent())
// 2. Créer le fichier de configuration pour un test contrôlé
val configFile = File(projectDir, "managed-jbake-context.yml")
configFile.writeText("site: { title: 'Mon Site de Test' }") // Contenu YAML simple
// 3. Exécuter la build
val runner = GradleRunner.create()
runner.withPluginClasspath()
runner.withArguments("printSiteConfig")
runner.withProjectDir(projectDir) // Spécifier le répertoire du projet de test
val result = runner.build()
// 4. Vérifier la sortie
assertTrue(result.output.contains("Site config path: managed-jbake-context.yml"))
}
Questo test fallirà finché il compito`printSiteConfig`non utilizzerà realmente il valore dell’estensione.
4.2. Finalizzare la Logica
Stiamo aggiornando l’attività in`SiteBakerPlugin.kt`affinché mostri il valore configurato:
project.tasks.register("printSiteConfig") { task ->
task.doLast {
println("Site config path: ${extension.configPath.get()}")
}
}
Tutti i test, unitari e funzionali, passano ora.
Conclusione
Seguendo un approccio TDD, abbiamo costruito un plugin in modo incrementale e sicuro. Ogni piccola funzionalità viene immediatamente validata da un test, dai test unitari rapidi ai test funzionali che convalidano l’integrazione completa.
Avendo cura di rendere i nostri test funzionantiermetici— ovvero, creando in modo programmato i file di configurazione necessari — costruiamo una rete di sicurezza estremamente robusta. Questa rigorosità ci protegge efficacemente dalle regressioni e ci dà molta fiducia per aggiungere funzionalità più complesse in seguito, come il parsing del file di configurazione YAML.
Questa base di test solidi è il punto di forza più prezioso per la manutenzione e l’evoluzione futura del plugin.
Articoli correlati
14 May 2026