Άρθρο 2 : Ανάπτυξη ενός Plugin Gradle με μια Προσέγγιση TDD
Publié le 24 September 2025
Σε αυτό το άρθρο, θα εξερευνήσουμε πώς να ρυθμίσουμε ένα σταθερό θεμέλιο ανάπτυξης για ένα πρόσθετο Gradle χρησιμοποιώντας μια προσέγγιση ανάπτυξης που καθοδηγείται από τα τεστ (TDD - Test-Driven Development). Αυτή η μέθοδος μας εξασφαλίζει ότι ο κώδικάς μας είναι ανθεκτικός, συντηρήσιμος και ικανοποιεί ακριβώς τις απαιτήσεις από την αρχή.
Η αρχικοποίηση του έργου
Το Gradle διευκολύνει πολύ τη δημιουργία ενός νέου plugin χάρη στην εντολή`gradle init`. Επιλέγοντάς να δημιουργήσουμε ένα "Gradle Plugin" με Kotlin, παίρνουμε μια έτοια δομή έργου, που περιλαμβάνει δύο βασικούς τύπους δοκιμών :
-
Δοκιμές μονάδων :Βρίσκονται σε`src/test`, επιτρέπουν τον έλεγχο απομονωμένων συστατικών του plugin μας, όπως η εσωτερική λογική μιας εργασίας ή η διαμόρφωση μιας επέκτασης. Είναι γρήγορα και δεν απαιτούν πληρό εκτέλεση του Gradle.
-
Λειτουργικοί Τεστ :Βρισκόμενοι σε`src/functionalTest`, Eles χ…`GradleRunner`Για να εκτελέσουμε μια πλήρη build Gradle σε ένα προσωρινό έργο δοκιμής. Αυτό μας επιτρέπει να ελέγξουμε την πραγματική συμπεριφορά του προσθέτου σε ένα ελεγχόμενο περιβάλλον.
2. Το πρώτο μας κύκλο ΤDD: Καταγραφή μίας εργασίας
Η πρώτη μας απαιτία είναι απλή: το plugin πρέπει να καταχωρίζει μια εργασία με όνομα`printSiteConfig`.
2.1. η prima δοκιμή (η δοκιμή που αποτύχει)
Συμφωνικά με το TDD, γράφουμε πρώτα ένα τεστ που ελέγχει την ύπαρξη αυτής της εργασίας. Σε`SiteBakerPluginTest.kt`(το αρχείο τεστ μονάδας μας), προσθέτουμε:
@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"))
}
Αυτός ο τεστ αποτυγχάνει, επειδή δεν έχουμε ακόμα γράψει καμία γραμμή κώδικα στον plugin μας.
2.2. Ο κώδικας στη συνέχεια (Πέραστε το τεστ)
Τώρα, γράφουμε τον ελάχιστο απαιτούμενο κώδικα στο`SiteBakerPlugin.kt`για να περάσει ο τεστ :
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
}
}
}
Ανακινούμε τα τεστ, και περνούν. Η πρώτη λειτουργία μας είναι επιβεβαιωμένη.
3. 2ος Κύκλος TDD: Προσθήκη Έπεκτασης DSL
Η επόμενη απαιτηση είναι να επιτρέπουμε στους χρήστες να ρυθμίζουν το plugin μας μέσω ενός μπλοκ DSL στους`build.gradle.kts`. Θέλουμε ένα μπλοκ`site { … }`όπου μπορεί να καθοριστεί μια διαδρομή ρύθμισης.
3.1. Το πρώτο τεστ
Προσθέτουμε έναν έλεγχο για να επαλυθεύσουμε ότι η_extension`site`είναι σωστά καταγεγραμμένη και μπορούμε να της θέσουμε μια αξία.
@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`ούτε η εγγραφή της επέκτασης υπάρχει.
3.2. Ο κώδικας έπειτα
Δημιουργούμε την κλάση`SiteBakerExtension.kt`και ενημερώνουμε`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
}
}
}
}
Οι δοκιμές επιτυχούν ξανά.
4. Ο Λειτουργικός Τέστος: Επικύρωση της Ενταξής
Τώρα που οι μονάδες έχουν δοκιμαστεί, πρέπει να βεβαιωθούμε ότι όλα λειτουργούν együtt σε μια πραγματική build. Αυτός είναι ο ρόλος του λειτουργικού τεστ σε`SiteBakerPluginFunctionalTest.kt`.
4.1. Δοκιμή ενσωμάτωσης: Δημιουργήστε ένα ελεγχόμενο περιβάλλον
Αυτό το τεστ θα προσομοιώσει ένα πραγματικό έργο που χρησιμοποιεί το πρόσθετο μας. Για να είναι αξιόπιστο, πρέπει να είναιαερωστός, δηλαδή δεν πρέπει να εξαρτάται από υφιστάμενα αρχεία στο σύστημα. Πρέπει να δημιουργήσει εαυτό του όλες τις απαιτούμενες συνθήκες για την εκτέλεσή του.
Ο τεστ θα άρα:
-
Δημιουργία ενός`build.gradle.kts`ο τεστ που χρησιμοποιεί το plugin μας και το DSL του.
-
Δημιουργήστε το αρχείο ρυθμίσεων (
managed-jbake-context.yml) το plugin πρέπει να διαβάσει. Αυτό είναι ένα κρίσιμο βήμα για την ανθεκτότητα του δοκιμίου. -
Εκτελέστε την εργασία`printSiteConfig`μέσω`GradleRunner`.
-
Ελέγξτε ότι η έξοδος της κατασκευής είναι σωστή.
Αντιγράφοντας ή δημιουργώντας αυτό το αρχείο ρύθμισης σε κάθε εκτέλεση, βεβαιόμαστε ότι ο έλεγχος είναιαναπαραγώσιμοΑυτό προστατεύει τις δοκιμές μας από τις παλινδρόμησες που μπορεί να σχετίζονται με την ανάγνωση του αρχείου.
@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"))
}
Αυτό το τεστ θα αποτύχει όσο η εργασία`printSiteConfig`Δεν θα χρησιμοποιήσει πραγματικά την τιμή της επέκτασης.
4.2. Τελειώστε τη λογική
Ενημερώνουμε την εργασία στο`SiteBakerPlugin.kt`για να εμφανίσει τη ρυθμισμένη τιμή :
project.tasks.register("printSiteConfig") { task ->
task.doLast {
println("Site config path: ${extension.configPath.get()}")
}
}
Όλοι οι τεστ, μονάδων και λειτουργικοί, τώρα περνούν.
Συμπέρασμα
Ακολουθώντας μια προσέγγιση TDD, έχουμε κατασκευάσει ένα πρόσθετο με σταδιακό και ασφαλή τρόπο. Κάθε μικρή λειτουργία επικυρώνεται αμέσως από έναν έλεγχο, από τα γρήγορα μονάδια έλεγχων μέχρι τα λειτουργικά έλεγχοι που επικυρώνουν την πλήρη ενσωμάτωση.
Προσέχοντας να κάνουμε τα τεστ μας λειτουργικάερμητικοί— δημιουργώντας προγραμματιστικά τα απαραίτητα αρχεία ρυθμίσεων — κτίζουμε ένα εξαιρετικά απρόσπαστο δίκτυο ασφαλείας. Αυτό το επίπεδο ακρίβειας μας προστατεύει αποτελεσματικά κατά των regressions και μας δίνει μεγάλη εμπιστοσύνη για να προσθέσουμε πιο پیچιδικές λειτουργίες στη συνέχεια, όπως η ανάλυση του αρχείου ρυθμίσεων YAML.
Αυτή η στερεά βάση δοκιμών είναι το πιοτιμότερο πλεονέκτημα για τη συντήρηση και τη μελλοντική εξέλιξη του πρόσθετου.