Publié par
Il y a 1 semaine · 20 minutes · Back, Craft

Automatiser l’exécution de ses scénarios Gherkin en Scala

Si l’exécution automatisée de spécifications par l’exemple (ou scénarios Gherkin) est monnaie courante et bien outillée dans beaucoup de langages tels que Java, Ruby ou C#, la recherche s’avère moins fructueuse en Scala. Dans cet article, nous vous proposons de parcourir les solutions qui s’offrent à vous pour exécuter automatiquement vos scénarios afin d’obtenir une réelle documentation vivante. Vous pourrez ainsi tirer profit à la fois du langage Scala et de la pratique du BDD.

Behavior-Driven Development : quelques rappels

Le BDD est une méthode dite de Knowledge Crunching. Elle s’appuie sur une collaboration forte entre experts métiers, développeurs et testeurs afin d’explorer le domaine du métier et les problèmes que l’on cherche à résoudre. La connaissance et le vocabulaire ainsi partagés sont capturés sous forme d’exemples concrets qui se concentrent sur le comportement de l’application. On parle ainsi de spécification par l’exemple et cette dernière est communément formulée via le langage Gherkin. L’exécution automatisée de ces scénarios permet d’obtenir une documentation vivante, i.e. dont on est certain qu’elle représente l’état de l’application à l’instant T.

La pratique du BDD se décompose généralement en 3 phases :

Copyright Paul Rayner - cucumber.io

  1. Explorer. Les développeurs, les testeurs et les experts métier collaborent pour explorer le domaine du métier et les problèmes à résoudre, sans contrainte sur le formalisme.
  2. Formaliser. Les « 3 amigos » collaborent pour produire des exemples concrets traduisant la connaissance et le vocabulaire partagés.
  3. Automatiser. Les scénarios sont exécutés automatiquement par un outil, produisant ainsi une documentation vivante.

Notre cas d’étude

Notre domaine métier : la gestion de conférences

Afin d’explorer les solutions d’automatisation possibles, nous articulerons nos exemples autour d’un système de gestion de conférences de type Eventbrite.
Dans sa forme actuelle, un gestionnaire peut :

  • créer une conférence ;
  • ajouter un type de siège avec un quota associé ;
  • publier une conférence pour rendre les sièges disponibles à la réservation.

A l’opposé, un déclarant peut passer une commande afin de réserver des sièges pour une conférence.
Pour le moment, il n’existe pas de réservation partielle. Si le quota est insuffisant, la commande est rejetée et aucune réservation n’est faite.

Notre modélisation : les agrégats Conference et Order

L’implémentation source de nos exemples utilise des concepts du Domain-Driven Design. Nous en rappellons ci-après une définition simple pour vous permettre de mieux comprendre les extraits de code. Si vous souhaitez en savoir plus, nous vous conseillons la lecture de la plaquette « Anatomy Of Domain-Driven Design » de Scott Millett.

Notre modèle du domaine est articulé autour de deux agrégats : Conference et Order.

Rappels

Un agrégat marque une frontière de cohérence autour d’une grappe d’objets en se basant sur des invariants (ou règles métier).

Conference

Un agrégat reçoit des commandes qui caractérisent une requête de l’extérieur.

PublishConference(id = "mix-it-18")

Le traitement d’une commande est transactionnel pour assurer l’intégrité de l’agrégat et de ses composants par rapport à ses invariants.
Les changements appliqués sont caractérisés par des événements métier. Ils notifient qu’un ensemble de modifications cohérentes vient d’avoir lieu sur un agrégat.

ConferencePublished(id = "mix-it-18")

Dans notre modélisation, l’agrégat Conference prend en charge :

  • la création, la mise à jour et la publication d’une conférence ;
  • l’ajout de sièges et des quotas associés ;
  • la disponibilité et la réservation de sièges.

L’agrégat Order, se charge lui de la prise de commande de la part d’un déclarant.

Agrégats et testabilité

Puisque nos agrégats marquent une frontière de cohérence métier isolée de tout détail technique, il est aisé de tester les invariants associés.
Prenons l’exemple de la publication d’une conférence :

"A conference" can "be published" in new Setup {

  // Given
  conferenceEventRepository.setHistory("mix-it-18", ConferenceCreated(name = "MixIT 2018", slug = "mix-it-18"))

  // When
  conferenceCommandHandler handle PublishConference(id = "mix-it-18")

  // Then
  conferenceEventRepository.getEventStream("mix-it-18") should contain inOrderOnly(
    ConferenceCreated(name = "MixIT 2018", slug = "mix-it-18"),
    ConferencePublished(id = "mix-it-18")
  )
}

Ce test aurait pu donner lieu à un scénario Gherkin car il correspond bien à un cas d’utilisation métier : un gestionnaire peut publier une conférence. Néanmoins, un cas d’utilisation se résume rarement à une action aussi simple et nécessite généralement la collaboration de plusieurs agrégats. C’est précisement dans ce cas que nous allons écrire notre scénario Gherkin et chercher à l’automatiser.Notre contexte courant est déclaré à l’aide d’une séquence d’événements. Notre intention est caractérisée par l’envoi d’une commande. Nos vérifications se font sur la séquence d’événements résultant de la décision de l’agrégat.

Notre scénario : la réservation de sièges pour un type dont le quota est suffisant

Nous souhaitons tester une prise de commande pour un type de siège dont le quota permet de la satisfaire. En somme, le cas idéal !
Le scénario, exprimé au format Gherkin du point de vue d’un déclarant, prend la forme suivante :

Feature : Place an order for a conference

   Scenario : Successfully place an order for a single conference seat type with enough quota
      Given a conference with a quota of 10 "Workshop" seats
      When a registrant place an order for 8 "Workshop" seats
      Then 8 "Workshop" seats are reserved

La partie Feature décrit la fonctionnalité attendue et est suivie d’un ou plusieurs exemples concrets (les Scenarios) permettant de l’expliciter. Ici, nous pourrions imaginer avoir d’autres scénarios liés aux cas d’erreur (quota insuffisant par exemple).

Ce scénario demande la collaboration des deux agrégats Conference et Order : la prise de commande va déclencher une réservation pour une conférence. Cette dernière déclenchera à son tour la mise à jour de la commande.

Solution n°1 – ScalaTest

Retrouvez les sources de cette solution sur la branche express-acceptance-tests-with-scalatest du système de gestion de conférence.

La première solution que nous allons explorer reste purement dans le monde Scala puisqu’elle est proposée par la répandue librairie de tests ScalaTest.
Cette dernière expose différents styles de tests dont un sied particulièrement à notre besoin : FeatureSpec.

class PlaceOrderForConference extends FeatureSpec with GivenWhenThen {
  feature("Place an order for a conference") {
    scenario("Successfully place an order for a single conference seat type with enough quota") {
        Given("""a conference with a quota of 10 "Workshop" seats""")
        When("""a registrant place an order for 8 "Workshop" seats""")
        Then("""8 "Workshop" seats are reserved""")
    }
  }
}

Ce style propose de décrire une fonctionnalité via différents scénarios qui sont représentés par nos méthodes de tests. Le trait GivenWhenThen apporte lui la syntaxe attendue pour décrire un scénario.

La mise en place

Pour l’utiliser, il nous faut tout d’abord ajouter la dépendance à notre projet :

libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.4" % "test"

Ensuite, nous pouvons implémenter notre scénario. Les détails techniques sont placés au même niveau que la description des étapes Given, When, Then. Le partage de variables est aisé puisqu’elles se trouvent au sein du même bloc de code.

class PlaceOrderForConference extends FeatureSpec with Matchers with GivenWhenThen {
  // ...

  feature("Place an order for a conference") {
    scenario("Successfully place an order for a single conference seat type with enough quota") {
      new Setup {

        val quota = 10
        val seatType = "Workshop"
        val conferenceEvents = Seq(
          ConferenceCreated(name = "MixIT", slug = "mix-it-18"),
          SeatsAdded(conferenceId = "mix-it-18", seatType, quota),
          ConferencePublished(id = "mix-it-18")
        )

        Given(s"""a conference with a quota of $quota "$seatType" seats""")
        eventSourcedRepository.setHistory("mix-it-18", conferenceEvents: _*)

        val seatsRequest = 8

        When(s"""a registrant place an order for $seatsRequest "$seatType" seats""")
        orderCommandHandler handle PlaceOrder(conferenceId = "mix-it-18", seatType -> seatsRequest)

        Then(s"""$seatsRequest "$seatType" seats are reserved""")
        eventSourcedRepository.getEventStream("mix-it-18") should contain theSameElementsInOrderAs (
          conferenceEvents :+ SeatsReserved(conferenceId = "mix-it-18", orderId = "ID-1", seatType -> seatsRequest)
        )

        eventSourcedRepository.getEventStream("ID-1") should contain inOrderOnly(
          OrderPlaced(orderId = "ID-1", conferenceId = "mix-it-18", seatType -> seatsRequest),
          SeatsReservationConfirmed(orderId = "ID-1", seatType -> seatsRequest)
        )
      }
    }
  }
}

Nous retrouvons ici la manière de tester aperçue précédemment. Notre contexte courant est décrit à l’aide d’une séquence d’événements (ou historique). Notre action est caractérisée par le traitement d’une commande (notre intention est de commander des places). Enfin, nous vérifions les décisions qui ont été prises par les agrégats Order et Conference en auscultant leurs flux d’événements respectifs.

Ceci fait, nos scénarios sont alors lançables via un simple sbt test, comme n’importe quels autres tests. Le scénario initial est alors disponible dans la console ou les logs :

[info] Feature: Place an order for a conference
[info]   Scenario: Successfully place an order for a single conference seat type with enough quota
[info]     Given a conference with a quota of 10 "Workshop" seats 
[info]     When a registrant place an order for 8 "Workshop" seats 
[info]     Then 8 "Workshop" seats are reserved

Pour rendre ce contenu exploitable en tant que documentation vivante, nous devons aller un peu plus loin et générer un rapport HTML. Pour ce faire, ajoutons la librairie suivante :

libraryDependencies += "org.pegdown" % "pegdown" % "1.6.0" % "test"

Ainsi que ces options dans la définition de notre build :

testOptions in Test ++= Seq(
   Tests.Argument(TestFrameworks.ScalaTest, "-h", "target/test-reports"),
   Tests.Argument(TestFrameworks.ScalaTest, "-o")
),

Ceci fait, un sbt test générera maintenant dans le dossier target/test-reports un rapport HTML prenant la forme suivante :

Les points positifs

  • Un outillage qui tient la route. Puisque nous manipulons du code Scala et que le lancement des tests se fait via sbt, cette solution est parfaitement intégrée à l’IDE utilisé, IntelliJ dans notre cas. Cela permet notamment de s’appuyer sur ce dernier pour automatiser le refactoring de nos scénarios.

Les points négatifs

  • Une documentation vivante à moitié morte. Le rapport généré se concentre sur les statistiques plutôt que sur le parcours des scénarios ou leur recherche. De plus, tous les tests lancés par sbt s’y retrouvent, qu’il s’agisse de scénarios ou de simples tests unitaires. Bref, difficile d’imaginer les utilisateurs métier s’en servir comme source de vérité.
Un plugin sbt existe afin de générer un rapport JGiven à partir des données fournies par ScalaTest.
Ce type de rapport sera discuté dans la suite de cet article, avec la solution du même nom.
  • Une lisibilité réduite. Les scénarios et la glu associée sont placés l’un à côté de l’autre, rendant la lecture des plus difficiles, que l’on s’intéresse au métier ou aux détails techniques. De plus, les étapes Given, When et Then ne sont pas réutilisables d’un scénario à l’autre, donnant lieu à une duplication accrue et une maintenabilité difficile.

Solution n°2 – Cucumber

Retrouvez les sources de cette solution sur la branche express-acceptance-tests-with-cucumber du système de gestion de conférence.

Scala a l’avantage de s’éxecuter sur la JVM et ainsi, de pouvoir s’interfacer avec la plupart des bibliothèques Java. Nous allons donc tirer profit des outils qui existent sur cette plateforme. Le plus connu d’entre eux est probablement Cucumber, par ailleurs disponible dans une multitude de langages (Ruby, JavaScript).

La mise en place

Pour l’utiliser, plusieurs dépendances sont requises :

libraryDependencies ++= Seq(
   "io.cucumber" % "cucumber-junit" % "2.3.0" % "test",
   "io.cucumber" %% "cucumber-scala" % "2.0.1" % "test",
   "junit" % "junit" % "4.12" % "test",
   "com.novocode" % "junit-interface" % "0.11" % "test" exclude("junit", "junit")
)

Notons ici que l’exécution de nos scénarios sera confiée au framework de tests JUnit, bien connu des développeurs Java. La dépendance junit-interface fournit le code nécessaire pour que sbt lance les tests JUnit de manière transparente.

Avec Cucumber, les scénarios sont écrits dans des fichiers texte avec l’extension .feature. Nous reprenons donc notre scénario dans un fichier PlaceOrderForConference.feature au sein du dossier src/test/resources/features :

Feature : Place an order for a conference

   Scenario : Successfully place an order for a single conference seat type with enough quota
      Given a conference with a quota of 10 "Workshop" seats
      When a registrant place an order for 8 "Workshop" seats
      Then 8 "Workshop" seats are reserved

L’implémentation des trois étapes, communément appellée glu, est placée au sein d’une classe que nous nommerons FeaturesStepDefinitions.

final class FeaturesStepDefinitions extends ScalaDsl with EN with Matchers {
  // ...

  val conferenceId = "mix-it-18"
  var conferenceEvents: Seq[Event] = _

  Given("""^a conference with a quota of (\d+) "([a-zA-Z]+)" seats$""") { (quota: Int, seatType: String) =>
    this.conferenceEvents = Seq(
      ConferenceCreated(name = "MixIT", slug = conferenceId),
      SeatsAdded(conferenceId, seatType, quota),
      ConferencePublished(id = conferenceId)
    )

    eventSourcedRepository.setHistory(conferenceId, conferenceEvents: _*)
  }

  When("""^a registrant place an order for (\d+) "([a-zA-Z]+)" seats$""") { (seatsRequest: Int, seatType: String) =>
    orderCommandHandler handle PlaceOrder(conferenceId, seatType -> seatsRequest)
  }

  Then("""^the (\d+) "([a-zA-Z]+)" seats are successfully reserved$""") { (seatsRequest: Int, seatType: String) =>
    eventSourcedRepository.getEventStream(conferenceId) should contain theSameElementsInOrderAs (
      conferenceEvents :+ SeatsReserved(conferenceId, orderId = "ID-1", seatType -> seatsRequest)
    )

    eventSourcedRepository.getEventStream("ID-1") should contain inOrderOnly(
      OrderPlaced(orderId = "ID-1", conferenceId, seatType -> seatsRequest),
      SeatsReservationConfirmed(orderId = "ID-1", seatType -> seatsRequest)
    )
  }
}

La correspondance entre le fichier texte et la glu se fait à l’aide d’expressions régulières. Ces dernières capturent les paramètres éventuels à l’aide de groupes, caractérisés par des couples de parenthèses. Le partage de variables entre les étapes se fait au travers de membres d’instances puisque les étapes Given, When et Then se trouvent dans des fonctions séparées.

Il est possible de placer les étapes Given, When et Then dans différents fichiers de définition. Cela a du sens lorsque le nombre de scénarios augmente et particulièrement si vous voulez regrouper des étapes qui sont cohérentes d’un point de vue métier. Par exemple, toutes les étapes Given liées à la définition d’une conférence peuvent être placées dans un fichier GivenAConferenceSteps.

Comment faire alors pour transférer des variables d’une étape à une autre (comme la séquence conferenceEvents, dans notre exemple) ? Cucumber propose de définir un objet World qui contiendra toutes les variables partagées par les étapes d’un scénario. Cet objet doit être injecté au travers de l’un des frameworks supportés (PicoContainer, Guice, Spring notamment).

Pour pouvoir lancer ces scénarios, il nous faut créer un TestRunner JUnit spécifique :

import cucumber.api.CucumberOptions
import cucumber.api.junit.Cucumber
import org.junit.runner.RunWith

@RunWith(classOf[Cucumber])
@CucumberOptions(
  features = Array("classpath:features"),
  glue = Array("classpath:cms.domain.features.steps"),
  plugin = Array("pretty")
)
class FeaturesTestRunner {}

Celui-ci spécifie notamment :

  • l’emplacement des scénarios et de la glu ;
  • les plugins ou options à appliquer.

Ceci fait, nos scénarios sont alors lançables via un simple sbt test, comme n’importe quel autre test. Le scénario initial est alors disponible dans la console ou les logs :

Feature: Place an order for a conference
  Scenario: Successfully place an order for a single conference seat type with enough quota # features/PlaceOrderForConference.feature:3
    Given a conference with a quota of 10 "Workshop" seats                                  # FeaturesStepDefinitions.scala:24
    When a registrant place an order for 8 "Workshop" seats                                 # FeaturesStepDefinitions.scala:34
    Then the 8 "Workshop" seats are successfully reserved                                   # FeaturesStepDefinitions.scala:38

1 Scenarios (1 passed)
3 Steps (3 passed)
0m0,251s

Pour rendre ce contenu exploitable en tant que documentation vivante, nous devons aller un peu plus loin et générer un rapport HTML. Pour ce faire, ajoutons le plugin html dans les options du TestRunner créé précedemment :

@RunWith(classOf[Cucumber])
@CucumberOptions(
  features = Array("classpath:features"),
  glue = Array("classpath:cms.domain.features.steps"),
  plugin = Array("pretty", "html:target/cucumber")
)
class FeaturesTestRunner {}

Ceci fait, un sbt test générera maintenant dans le dossier target/cucumber un rapport HTML prenant la forme suivante :

Les points positifs

  • Une solution populaire et éprouvée.
  • Une solution lisible et maintenable. La séparation entre scénarios et glu permet une lecture aisée, que l’on s’intéresse au métier ou aux détails techniques. C’est particulièrement vrai pour les scénarios qui ne subissent aucune traduction étant exprimés sous la forme de fichiers texte. La séparation des étapes Given, When, Then permet par ailleurs une bonne réutilisabilité et facilite la maintenance du code de glu. Seul le partage de l’état au travers d’une variable « fourre-tout » peut s’avérer ennuyeux sur le long terme si l’on n’y prête pas attention.

Les points négatifs

  • Une documentation vivante déjà morte. Le rapport dans sa version basique est quasi inexploitable. Des plugins complémentaires permettent de retrouver quelque chose de centré sur les statistiques, semblable à la solution n°1. Mais ces derniers sont, soit basés sur Maven et difficilement exploitables avec sbt, soit basés sur un outil externe tel que Jenkins. Bref, là aussi, difficile d’imaginer les utilisateurs métier s’en servir comme source de vérité.
  • Un outillage balbutiantS’il existe un plugin Cucumber pour IntelliJ, sa maturité n’est pas suffisante pour espérer obtenir la productivtié connue dans d’autres langages comme Java. Par exemple, il est impossible de créer automatiquement les étapes Given, When, Then à partir d’un scénario texte. De même, on ne peut se baser sur l’IDE pour renommer une étape utilisée dans plusieurs scénarios, la faute aux expressions régulières qui ne sont pas reconnues comme des identifiants valides :

Solution n°3 – JGiven

Retrouvez les sources de cette solution sur la branche master du système de gestion de conférence.

Explorons maintenant une autre solution tirant profit de l’interfaçage avec les bibliothèques Java.
Le petit dernier des outils d’automatisation de scénarios Gherkin s’appelle JGiven et propose une approche différente de celle portée par ses concurrents, Cucumber en tête.

La mise en place

Pour l’utiliser, plusieurs dépendances sont nécessaires. Comme dans la solution précédente, l’exécution de nos scénarios est confiée au framework de tests JUnit.

libraryDependencies ++= Seq(
  "com.tngtech.jgiven" % "jgiven-junit" % "0.15.3" % "test",
  "junit" % "junit" % "4.12" % "test"
)

Avec JGiven, les scénarios sont écrits avec du code Java, sans passer par un fichier texte :

class PlaceAnOrderForConference
  extends ScenarioTest[GivenAConference, WhenRegistrantPlaceOrder, ThenSeatsAreReserved] with JUnitSuiteLike {

  @Test
  def successfully_place_an_order_for_a_single_conference_seat_type_with_enough_quota(){
    given a_conference_with_a_quota_of_$_$_seats(10, "Workshop")

    when a_registrant_place_an_order_for_$_$_seats(8, "Workshop")

    `then` $_$_seats_are_reserved(8, "Workshop")
  }
}

Le trait JUnitSuiteLike de ScalaTest permet d’écrire ses tests « à la JUnit », ce qui permet d’annoter nos méthodes avec @org.junit.Test.
Le classe com.tngtech.jgiven.junit.ScenarioTest marque notre intention d’écrire des scénarios Gherkin.

La fonctionnalité à décrire est déduite du nom de la classe de test et les scénarios du nom des méthodes, écrit en Snake case ou Camel case. Les étapes sont représentées par des méthodes classiques que nous avons ici décidé de placer dans trois classes GivenAConferenceWhenRegistrantPlaceOrder et ThenSeatsAreReserved. Lors de leur appel, la valeur de leurs paramètres viendra remplacer les symboles $ présents dans leur nom. La classe GivenAConference est par exemple définie comme suit :

class GivenAConference extends Stage[GivenAConference] {
  // ...

  @ProvidedScenarioState var history: Seq[Event] = _

  def a_conference_with_a_quota_of_$_$_seats(quota: Int, @Quoted seatType: String){
    history = Seq(
      ConferenceCreated(name = "MixIT", conferenceId),
      SeatsAdded(conferenceId, seatType, quota),
      ConferencePublished(conferenceId)
    )

    eventSourcedRepository.setHistory("mix-it-18", history: _*)
  }
}

La classe com.tngtech.jgiven.Stage indique que nous définissons des étapes de scénarios. Remarquons également l’annotation ProvidedScenarioState sur notre membre d’instance history : elle indique que ce groupement d’étapes est en charge d’en fournir la valeur qui sera rendue disponible dans les étapes ultérieures. Nous récupérons cette valeur dans notre groupement d’étapes ThenSeatsAreReserved, en utilisant l’annotation inverse ExpectedScenarioState :

class ThenSeatsAreReserved extends Stage[ThenSeatsAreReserved] with Matchers {
  // ...

  @ExpectedScenarioState var conferenceHistory: Seq[Event] = _

  def $_$_seats_are_reserved(seatsRequest: Int, @Quoted seatType: String) ={
    eventSourcedRepository.getEventStream(conferenceId) should contain theSameElementsInOrderAs
      conferenceHistory :+ SeatsReserved(conferenceId, orderId = "ID-1", seatType -> seatsRequest)

    eventSourcedRepository.getEventStream("ID-1") should contain inOrderOnly(
      OrderPlaced(orderId = "ID-1", conferenceId, seatType -> seatsRequest),
      SeatsReservationConfirmed(orderId = "ID-1", seatType -> seatsRequest)
    )
  }
}

Ceci fait, nos scénarios sont alors lançables via un simple sbt test, comme n’importe quels autres tests. Le scénario initial est alors disponible dans la console ou les logs :

Test Class: cms.domain.features.PlaceAnOrderForConference

 Successfully place an order for a single conference seat type with enough quota
   Given a conference with a quota of 10 "Workshop" seats
    When a registrant place an order for 8 "Workshop" seats
    Then 8 "Workshop" seats are reserved

Pour rendre ce contenu exploitable en tant que documentation vivante, nous devons aller un peu plus loin et générer un rapport HTML. Pour ce faire, ajoutons la dépendance fournie par JGiven :

libraryDependencies += "com.tngtech.jgiven" % "jgiven-html5-report" % "0.15.3" % "test"

Nous créons ensuite une tâche sbt dédiée qui va lancer les tests et générer le rapport HTML :

lazy val livingDocumentation = taskKey[Unit]("Generate HTML5 report containing business features and scenarios.")
livingDocumentation := Def.sequential(
  test in Test,
  runMain in Test toTask " com.tngtech.jgiven.report.ReportGenerator --targetDir=target/jgiven-reports/html"
).value

Notre rapport est alors disponible dans le dossier target/jgiven-reports/html via un sbt livingDocumentation :

Les points positifs

  • Une documentation bien vivante ! Le rapport généré se concentre sur le parcours des scénarios que ce soit via la recherche ou via la hiérarchie de tags. Les filtres et groupements proposés sont nombreux de telle sorte que cela devient un argument de poids pour voir le métier s’en servir comme source de vérité.
  • Un outillage qui tient la route. Puisque nous manipulons du code Scala/Java et que le lancement des tests se fait via sbt/Junit, cette solution est parfaitement intégrée à l’IDE utilisé, IntelliJ dans notre cas. Cela permet notamment de s’appuyer sur ce dernier pour automatiser le refactoring de nos scénarios, d’autant plus qu’il n’existe aucun fichier texte ni expression régulière.

Les points négatifs

  • Des choix forts qui peuvent surprendre. Faire le choix de se passer de scenarios au format texte n’est pas forcément compris dans toutes les équipes ou applicable dans tous les contextes. Par exemple, si vos experts métier sont habitués à écrire les scénarios texte seuls dans leur coin, cette solution ne leur conviendra guère. Il vous faudra travailler en priorité sur la collaboration avec eux avant d’imaginer pouvoir la mettre en place.

Conclusion

Dans cet article, nous avons vu ensemble trois solutions pour automatiser l’exécution de nos scénarios Gherkin en Scala (ScalaTest, Cucumber et JGiven). Pour chacune, nous avons vu comment la mettre en place ainsi que les avantages et inconvénients associés.

Sachez que de notre côté, nous favorisons la solution basée sur JGiven. Elle est la seule à offrir une réelle documentation vivante, exploitable par le métier comme source de vérité. De plus, l’outillage existant permet d’être productif dans l’écriture, le lancement des tests et la génération du rapport.

Si le choix de l’outil vous revient, gardez en tête que l’automatisation de vos scénarios n’est que la partie émergée de l’icerberg qu’est le BDD. N’hésitez pas à mettre l’accent sur la collaboration avec votre métier ; là se trouve la plus grosse source de valeur pour eux comme pour vous, dans la compréhension partagée des concepts et des termes métiers implémentés au quotidien.

Clément Héliou

Pragmastism Driven Developer passionate about xDD (TDD/BDD/DDD), clean architectures (Event Sourcing, Hexagonal Architecture and al.) and code quality. Enthusiastic defender of knowledge sharing and human skills.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *