Dalam artikel ini, kami akan menjelajahi cara menyiapkan dasar pengembangan yang kuat untuk plugin Gradle dengan menggunakan pendekatan Pengembangan Berbasis Tes (TDD - Test-Driven Development). Metode ini memastikan bahwa kode kita kuat, dapat dipelihara, dan memenuhi persyaratan secara tepat sejak awal.

Inisialisasi Proyek

Gradle sangat memudahkan pembuatan plugin baru melalui perintah`gradle init`. Memilih untuk membuat "Gradle Plugin" dengan Kotlin, kita mendapatkan struktur proyek yang siap digunakan, mencakup dua jenis tes yang penting:

  • Uji Unit :Berada di`src/test`, mereka memungkinkan untuk memvalidasi komponen terisolasi dari plugin kami, seperti logika internal dari suatu tugas atau konfigurasi ekstensi. Mereka cepat dan tidak memerlukan eksekusi lengkap Gradle.

  • Uji Fungsional :Berada di`src/functionalTest`, mereka menggunakan`GradleRunner`untuk menjalankan build Gradle lengkap dalam proyek uji sementara. Hal ini memungkinkan kami untuk memverifikasi perilaku nyata plugin dalam lingkungan yang terkendali.

2. Siklus TDD Pertama Kami: Mencatat sebuah tugas

Persyaratan pertama kami sederhana: plugin harus mencatat sebuah tugas bernama`printSiteConfig`.

2.1. Uji Coba Pertama (Uji Coba yang Gagal)

Sesuai dengan TDD, kita pertama kali menulis sebuah tes yang memverifikasi keberadaan tugas ini. Di`SiteBakerPluginTest.kt`(file tes unit kami), kami menambahkan :

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

Uji coba ini gagal karena kami belum menulis kode apa pun dalam plugin kami.

2.2. Kode Selanjutnya (Menjalankan Uji)

Sekarang, kami menulis kode minimum yang diperlukan di`SiteBakerPlugin.kt`agar test lulus :

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

Kami memulai ulang tes, dan mereka lulus. Fitur pertama kami telah divalidasi.

3. Siklus Kedua TDD: Menambahkan Ekstensi DSL

Persyaratan berikutnya adalah memungkinkan pengguna untuk mengonfigurasi plugin kami melalui blok DSL di`build.gradle.kts`. Kami ingin sebuah blok`site { …​ }`di mana seseorang dapat menentukan jalur konfigurasi.

3.1. Uji coba pertama

Kami menambahkan uji untuk memverifikasi bahwa ekstensi`site`Tercatat dengan baik dan nilai dapat diberikan kepadanya.

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

Uji ini gagal karena tidak kelas`SiteExtension`baik tidak ada pendaftaran ekstensi

3.2. Kode Berikutnya

Kami membuat kelas`SiteBakerExtension.kt`dan kami memperbarui`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
            }
        }
    }
}

Tes kembali berhasil.

4. Tes Fungsional: Memvalidasi Integrasi

Sekarang setelah unit diuji, kita harus memastikan semuanya berfungsi bersama dalam build yang sebenarnya. Ini adalah peran dari pengujian fungsional dalam`SiteBakerPluginFunctionalTest.kt`.

4.1. Pengujian Integrasi: Membuat Lingkungan Terkontrol

Tes ini akan mensimulasikan proyek nyata yang menggunakan plugin kita. Agar dapat dipercaya, ia harushermetis, artinya ia tidak boleh bergantung pada file yang ada di sistem. Dia harus menciptakan sendiri semua kondisi yang diperlukan untuk eksekusi.

Uji coba maka:

  1. Membuat`build.gradle.kts`uji yang menggunakan plugin kami dan DSL-nya.

  2. Membuat file konfigurasi (managed-jbake-context.yml) yang seharusnya dibaca oleh plugin. Ini adalah langkah krusial untuk ketangguhan uji.

  3. Jalankan tugas`printSiteConfig`melalui`GradleRunner`.

  4. Pastikan bahwa output build-nya benar.

Salin atau buat file konfigurasi ini setiap kali dijalankan, kami memastikan bahwa pengujian adalahdapat direproduksidan tidak bergantung pada keadaan eksternal. Ini mengamankan tes kita dari regresi yang mungkin terkait dengan pembacaan 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"))
}

Test ini akan gagal selama tugas`printSiteConfig`Tidak akan sebenarnya menggunakan nilai ekstensi.

4.2. Menyelesaikan Logika

Kami sedang memperbarui tugas di`SiteBakerPlugin.kt`supaya ia menampilkan nilai yang dikonfigurasi:

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

Semua tes, unitari dan fungsional, lulus sekarang.

Kesimpulan

Dengan mengikuti pendekatan TDD, kami telah membangun sebuah plugin secara bertahap dan aman. Setiap fungsi kecil langsung divalidasi oleh sebuah tes, dari tes unit yang cepat hingga tes fungsional yang memvalidasi integrasi lengkap.

Dengan berhati-hati untuk membuat tes kami fungsionalhermetis— terutama dengan membuat secara programmatis file konfigurasi yang diperlukan — kami membangun jaring keamanan yang sangat kuat. Ketelitian ini melindungi kami secara efektif dari regresi dan memberi kami kepercayaan besar untuk menambahkan fitur-fitur yang lebih kompleks nantinya, seperti parsing file konfigurasi YAML.

Basis tes yang kuat ini adalah aset paling berharga untuk pemeliharaan dan perkembangan masa depan plugin.

Artikel terkait