U ovom članku bićemo istraživali kako postaviti čvrsto temelj za razvoj Gradle plugina koristeći pristup Test-Driven Development (TDD). Ova metoda nam osigurava da je naš kod robustan, Drživ i tačno odgovara zahtevima od samog početka.

1. Иницијализација пројекта

Gradle olakšava kreiranje novog plugina zahvaljujući naredbi`gradle init`. Izborom da kreiramo "Gradle Plugin" uz Kotlin, dobijamo spremnu strukturu projekta koja uključuje dva ključna tipa testova :

  • Jedinični testovi :položeni u`src/test`, oni omogućavaju validaciju izolovanih komponenti naše dodatka, kao što je interna logika zadatka ili konfiguracija dodatka. Oni su brzi i ne zahtevaju punu izvršavanje Gradle.

  • Funkcionalni testovi :Smješteni u`src/functionalTest`, oni koriste`GradleRunner`Za izvršavanje kompletne Gradle gradnje u privremenom testnom projektu. Ovo nam omogućava da proverimo stvarno ponašanje plugina u kontrolisanom okruženju.

2. Nas prvi TDD ciklus: unesi zadatak

Naša prva zahteva je jednostavna: plugin mora da zabilježi nazvan zadatak`printSiteConfig`.

2.1. Први тест (Тест који није успео)

U skladu sa TDD-om, prvo pišemo test koji proverava da li postoji ovaj zadatak. U`SiteBakerPluginTest.kt`(naš fajl za jedinični test), dodajemo :

@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"))
}

Ovaj test ne uspeva, jer smo još nijedan kod napisali u našem pluginu.

2.2. Kod nakon (Uspostavi test)

Sada, pišemo minimalno potrebni kod u`SiteBakerPlugin.kt`da bi test prošao :

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
        }
    }
}

Ponovo pokrećemo testove, i oni prolaze. Naša prva funkcionalnost je validovana.

3. Drugi ciklus TDD: Dodati DSL ekstenziju

Sledeći zahtev je omogućiti korisnicima da konfigurisu naš plugin putem DSL bloka u njihovom`build.gradle.kts`. Želimo blok`site { …​ }`где може да се наведе путања конфигурације.

3.1. Тест први

Dodajemo test da proverimo da li je ekstenzija`site`Je dobro evidentirano i možemo joj dodeliti vrednost.

@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()
    )
}

Овај тест не успева ни класа`SiteExtension`ni evidencija ekstenzije ne postoji

3.2. Sledeći kod

Kreiramo klasu`SiteBakerExtension.kt`i ažuriramo`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
            }
        }
    }
}

Testovi ponovo prolaze.

4. Funkcionalni test: Validacija integracije

Sada kada su jedinice testirane, moramo se uveriti da sve radi zajedno u stvarnoj gradnji. To je uloga funkcionalnog testa u`SiteBakerPluginFunctionalTest.kt`.

4.1. Test integracije: Kreirati kontrolisano okruženje

Ovaj test će simulirati pravi projekat koji koristi naš plugin. Da bi bio pouzdan, mora bitihermetski, to jest da ne smije zavisiti od postojećih datoteka na sistemu. On mora sam stvoriti sve neophodne uslove za njegovo izvršenje.

Test će dakle :

  1. Napraviti jedan`build.gradle.kts`test koji koristi naš plugin i njegov DSL.

  2. Kreirajte datoteku konfiguracije(managed-jbake-context.yml) što plugin treba da pročita. Ovo je ključni korak za robustnost testa.

  3. Izvršiti zadatak`printSiteConfig`преку`GradleRunner`.

  4. Proveri da li je izlaz izgradnje ispravan.

Kopirandom ili stvaranjem ove datoteke konfiguracije pri svakom pokretanju, osiguravamo da je testповторљивi ne zavisi od vanjskog stanja. Ovo zaštičava naše testove od regresija koje bi mogle biti povezane sa čitanjem fajla.

@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"))
}

Ovaj test će neudati dok je zadatak`printSiteConfig`Neće zapravo koristiti vrednost ekstenzije.

4.2. Završiti logiku

Ažuriramo zadatak u`SiteBakerPlugin.kt`da bi prikazala konfigurisanu vrednost:

project.tasks.register("printSiteConfig") { task ->
    task.doLast {
        println("Site config path: ${extension.configPath.get()}")
    }
}

Svi testovi, jedinični i funkcionalni, sada prolaze.

Zaključak

Sledeći pristup TDD, izgradili smo plugin inkrementalno i bezbedno. Svaka mala funkcionalnost se odmah validira testem, od brzi unitarni testovi do funkcionalnih testova koji validiraju kompletnu integraciju.

Pažljivo čineći naše teste funkcionalnimhermetски— naročito programski kreiranjem potrebnih konfiguraционных fajlova — gradimo ekstremno jaku mrežu bezbednosti. Ova riguroznost nam efikasno štiti od regresija i nam daje veliko poverenje da dodajemo složenije funkcionalnosti kasnije, kao što je parsiranje YAML konfiguraционог fajla.

Ova čvrsta baza testova je najvažnija vrednost za održavanje i budući razvoj plugin-a.

Повезани чланци