Gherkin - Langage de Spécification BDD
Introduction
Gherkin est un langage de spécification lisible par les humains qui permet de décrire le comportement d'une application de manière structurée. Il est principalement utilisé dans le cadre du Behavior-Driven Development (BDD) avec des outils comme Cucumber, SpecFlow, ou Behave.
Principes du BDD
@startuml
package "BDD - Behavior Driven Development" {
[Business] as business
[Développeurs] as dev
[Testeurs] as qa
[Gherkin] as gherkin
}
business --> gherkin : Écrit les scénarios
dev --> gherkin : Implémente les steps
qa --> gherkin : Valide les tests
note right of gherkin
Langage commun compréhensible
par tous les acteurs du projet
end note
@enduml
Avantages de Gherkin
- Lisible : Compréhensible par les non-techniques (métier, PO, QA)
- Structuré : Format standardisé et prévisible
- Exécutable : Les scénarios deviennent des tests automatisés
- Documentation vivante : Les tests documentent le comportement attendu
- Collaboration : Langage commun entre métier et technique
Syntaxe de Base
Structure d'un Fichier Gherkin
# Commentaire
Feature: Titre de la fonctionnalité
Description de la fonctionnalité
sur plusieurs lignes si nécessaire
Background:
Given contexte commun à tous les scénarios
Scenario: Titre du scénario
Given contexte initial
When action effectuée
Then résultat attendu
Scenario Outline: Scénario avec exemples
Given un contexte avec <paramètre>
When une action avec <valeur>
Then un résultat <attendu>
Examples:
| paramètre | valeur | attendu |
| param1 | val1 | result1 |
| param2 | val2 | result2 |
Mots-Clés Principaux
| Mot-clé | Signification | Usage |
|---|---|---|
Feature |
Fonctionnalité | Décrit la fonctionnalité testée |
Scenario |
Scénario | Un cas de test spécifique |
Given |
Étant donné | Contexte initial / préconditions |
When |
Quand | Action déclenchée |
Then |
Alors | Résultat attendu / assertions |
And |
Et | Continue l'étape précédente |
But |
Mais | Négation ou exception |
Background |
Contexte | Étapes communes à tous les scénarios |
Scenario Outline |
Plan de scénario | Scénario paramétré |
Examples |
Exemples | Données pour le Scenario Outline |
Feature (Fonctionnalité)
Syntaxe
Feature: Authentification utilisateur
En tant qu'utilisateur
Je veux pouvoir me connecter à l'application
Afin d'accéder à mes données personnelles
Structure Recommandée
Feature: [Nom de la fonctionnalité]
En tant que [rôle]
Je veux [action]
Afin de [bénéfice]
Exemple Complet
Feature: Gestion du panier d'achat
En tant que client
Je veux pouvoir ajouter et retirer des articles de mon panier
Afin de préparer ma commande avant de payer
Le panier doit permettre :
- L'ajout d'articles
- La modification des quantités
- La suppression d'articles
- Le calcul automatique du total
Scenario (Scénario)
Scénario Simple
Scenario: Connexion réussie avec des identifiants valides
Given l'utilisateur est sur la page de connexion
And l'utilisateur a un compte avec l'email "john@example.com"
When l'utilisateur saisit "john@example.com" dans le champ email
And l'utilisateur saisit "Password123!" dans le champ mot de passe
And l'utilisateur clique sur le bouton "Se connecter"
Then l'utilisateur est redirigé vers la page d'accueil
And un message "Bienvenue John" est affiché
Diagramme de Flux
@startuml
start
:Given: Page de connexion;
:Given: Compte existant;
:When: Saisie email;
:When: Saisie mot de passe;
:When: Clic sur "Se connecter";
:Then: Redirection accueil;
:Then: Message de bienvenue;
stop
@enduml
Given, When, Then
Given (Étant donné) - Contexte
Définit le contexte initial et les préconditions.
Given l'utilisateur est connecté
Given le panier contient 3 articles
Given la base de données contient les utilisateurs suivants:
| nom | email | rôle |
| Alice | alice@example.com | admin |
| Bob | bob@example.com | user |
When (Quand) - Action
Décrit l'action ou l'événement qui se produit.
When l'utilisateur clique sur "Ajouter au panier"
When l'utilisateur soumet le formulaire
When le système reçoit une notification
Then (Alors) - Résultat
Spécifie le résultat attendu ou les assertions.
Then le panier contient 4 articles
Then un message de confirmation est affiché
Then l'utilisateur reçoit un email de confirmation
Then la réponse HTTP a le statut 200
And / But - Continuité
Scenario: Ajout d'un article au panier
Given l'utilisateur est sur la page produit
And le produit est en stock
When l'utilisateur clique sur "Ajouter au panier"
Then le panier contient 1 article
And le prix total est de 29.99€
But le stock du produit est décrémenté de 1
Background (Contexte Commun)
Le Background permet de définir des étapes communes à tous les scénarios d'une Feature.
Feature: Gestion des commandes
Background:
Given l'utilisateur "john@example.com" est connecté
And l'utilisateur a l'adresse de livraison suivante:
| rue | 123 Rue de la Paix |
| ville | Paris |
| code postal | 75001 |
Scenario: Passer une commande avec un article
Given le panier contient 1 article
When l'utilisateur valide sa commande
Then la commande est créée avec succès
Scenario: Passer une commande avec plusieurs articles
Given le panier contient 3 articles
When l'utilisateur valide sa commande
Then la commande est créée avec succès
@startuml
start
partition Background {
:Connexion utilisateur;
:Adresse de livraison;
}
partition "Scenario 1" {
:1 article dans panier;
:Validation commande;
:Commande créée;
}
partition "Scenario 2" {
:3 articles dans panier;
:Validation commande;
:Commande créée;
}
stop
@enduml
Scenario Outline (Scénario Paramétré)
Permet d'exécuter le même scénario avec différents jeux de données.
Feature: Calculatrice
Scenario Outline: Addition de deux nombres
Given j'ai saisi <nombre1> dans la calculatrice
When j'additionne <nombre2>
Then le résultat devrait être <résultat>
Examples:
| nombre1 | nombre2 | résultat |
| 2 | 3 | 5 |
| 10 | 15 | 25 |
| -5 | 5 | 0 |
| 100 | 200 | 300 |
Plusieurs Tables d'Exemples
Scenario Outline: Connexion avec différents types d'utilisateurs
Given l'utilisateur "<email>" avec le mot de passe "<password>"
When l'utilisateur se connecte
Then l'utilisateur a le rôle "<rôle>"
And l'utilisateur voit la page "<page>"
Examples: Utilisateurs valides
| email | password | rôle | page |
| admin@example.com | Admin123! | admin | dashboard|
| user@example.com | User123! | user | home |
Examples: Utilisateurs invalides
| email | password | rôle | page |
| bad@example.com | Wrong! | none | login |
Data Tables (Tables de Données)
Table Simple
Given les utilisateurs suivants existent:
| nom | email | âge |
| Alice | alice@example.com | 25 |
| Bob | bob@example.com | 30 |
| Charlie| charlie@example.com| 35 |
Table Verticale
Given un utilisateur avec les informations suivantes:
| nom | John Doe |
| email | john@example.com |
| téléphone | +33612345678 |
| adresse | 123 Rue de Paris |
| code postal | 75001 |
Doc Strings (Chaînes Multi-lignes)
Pour les données textuelles volumineuses.
Scenario: Envoi d'un email
Given un utilisateur connecté
When l'utilisateur envoie un email avec le contenu:
"""
Bonjour,
Ceci est un email de test
avec plusieurs lignes.
Cordialement,
John
"""
Then l'email est envoyé avec succès
JSON / XML
Scenario: Création d'un utilisateur via API
When je fais une requête POST à "/api/users" avec le body:
"""json
{
"name": "John Doe",
"email": "john@example.com",
"age": 30,
"roles": ["user", "admin"]
}
"""
Then la réponse a le statut 201
And la réponse contient l'id de l'utilisateur créé
Tags (Étiquettes)
Les tags permettent d'organiser et de filtrer les scénarios.
@authentification @critique
Feature: Connexion utilisateur
@smoke @rapide
Scenario: Connexion réussie
Given l'utilisateur a des identifiants valides
When l'utilisateur se connecte
Then l'utilisateur est authentifié
@sécurité
Scenario: Connexion échouée avec mot de passe incorrect
Given l'utilisateur a un email valide
When l'utilisateur se connecte avec un mauvais mot de passe
Then un message d'erreur est affiché
@wip @ignore
Scenario: Connexion avec authentification à deux facteurs
# Work in progress - à implémenter
Utilisation des Tags
# Exécuter uniquement les tests @smoke
cucumber --tags @smoke
# Exécuter tous sauf @wip
cucumber --tags "not @wip"
# Exécuter @critique ET @authentification
cucumber --tags "@critique and @authentification"
# Exécuter @smoke OU @rapide
cucumber --tags "@smoke or @rapide"
Exemples Complets
E-commerce
@ecommerce @panier
Feature: Gestion du panier d'achat
En tant que client
Je veux gérer mon panier d'achat
Afin de préparer ma commande
Background:
Given l'utilisateur est connecté
And le catalogue contient les produits suivants:
| id | nom | prix | stock |
| 1 | Laptop | 999 | 10 |
| 2 | Souris | 29 | 50 |
| 3 | Clavier | 79 | 30 |
@ajout
Scenario: Ajouter un article au panier vide
Given le panier est vide
When l'utilisateur ajoute le produit "Laptop" au panier
Then le panier contient 1 article
And le total du panier est de 999€
@quantité
Scenario Outline: Modifier la quantité d'un article
Given le panier contient 1 "Laptop"
When l'utilisateur change la quantité à <quantité>
Then le panier contient <quantité> "Laptop"
And le total du panier est de <total>€
Examples:
| quantité | total |
| 2 | 1998 |
| 3 | 2997 |
| 0 | 0 |
@suppression
Scenario: Supprimer un article du panier
Given le panier contient les articles suivants:
| produit | quantité |
| Laptop | 1 |
| Souris | 2 |
When l'utilisateur supprime "Laptop" du panier
Then le panier contient 1 type d'article
And le panier contient 2 "Souris"
But le panier ne contient pas "Laptop"
API REST
@api @utilisateurs
Feature: API de gestion des utilisateurs
En tant que développeur
Je veux tester l'API utilisateurs
Afin de garantir son bon fonctionnement
Background:
Given l'API est disponible à "http://localhost:8080"
And j'ai un token d'authentification valide
@get
Scenario: Récupérer la liste des utilisateurs
When je fais une requête GET à "/api/users"
Then la réponse a le statut 200
And la réponse contient une liste d'utilisateurs
And chaque utilisateur a les champs suivants:
| id |
| name |
| email |
@post @création
Scenario: Créer un nouvel utilisateur
When je fais une requête POST à "/api/users" avec:
"""json
{
"name": "Alice Martin",
"email": "alice@example.com",
"password": "SecurePass123!"
}
"""
Then la réponse a le statut 201
And la réponse contient l'id du nouvel utilisateur
And un email de confirmation est envoyé à "alice@example.com"
@put @modification
Scenario: Modifier un utilisateur existant
Given un utilisateur existe avec l'id 42
When je fais une requête PUT à "/api/users/42" avec:
"""json
{
"name": "Alice Dupont"
}
"""
Then la réponse a le statut 200
And l'utilisateur 42 a le nom "Alice Dupont"
@delete @suppression
Scenario: Supprimer un utilisateur
Given un utilisateur existe avec l'id 42
When je fais une requête DELETE à "/api/users/42"
Then la réponse a le statut 204
And l'utilisateur 42 n'existe plus
Implémentation avec Cucumber (Java)
Structure du Projet
src/
├── test/
│ ├── java/
│ │ ├── steps/
│ │ │ ├── AuthenticationSteps.java
│ │ │ └── CartSteps.java
│ │ ├── runners/
│ │ │ └── CucumberRunner.java
│ │ └── config/
│ │ └── TestConfig.java
│ └── resources/
│ └── features/
│ ├── authentication.feature
│ └── cart.feature
Step Definitions (Java)
import io.cucumber.java.en.Given;
import io.cucumber.java.en.When;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.And;
public class AuthenticationSteps {
private User currentUser;
private LoginPage loginPage;
private HomePage homePage;
@Given("l'utilisateur est sur la page de connexion")
public void userIsOnLoginPage() {
loginPage = new LoginPage();
loginPage.open();
}
@Given("l'utilisateur a un compte avec l'email {string}")
public void userHasAccountWithEmail(String email) {
currentUser = userRepository.findByEmail(email);
assertThat(currentUser).isNotNull();
}
@When("l'utilisateur saisit {string} dans le champ email")
public void userEntersEmail(String email) {
loginPage.enterEmail(email);
}
@When("l'utilisateur saisit {string} dans le champ mot de passe")
public void userEntersPassword(String password) {
loginPage.enterPassword(password);
}
@When("l'utilisateur clique sur le bouton {string}")
public void userClicksButton(String buttonText) {
loginPage.clickButton(buttonText);
}
@Then("l'utilisateur est redirigé vers la page d'accueil")
public void userIsRedirectedToHomePage() {
homePage = new HomePage();
assertThat(homePage.isDisplayed()).isTrue();
}
@Then("un message {string} est affiché")
public void messageIsDisplayed(String message) {
assertThat(homePage.getWelcomeMessage()).contains(message);
}
}
Cucumber Runner
import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;
import org.junit.runner.RunWith;
@RunWith(Cucumber.class)
@CucumberOptions(
features = "src/test/resources/features",
glue = {"steps", "config"},
plugin = {
"pretty",
"html:target/cucumber-reports/cucumber.html",
"json:target/cucumber-reports/cucumber.json"
},
tags = "not @wip"
)
public class CucumberRunner {
}
Bonnes Pratiques
1. Écrire des Scénarios Déclaratifs
❌ Mauvais (impératif) :
Scenario: Connexion
Given je suis sur "http://example.com/login"
When je clique sur le champ "email"
And je tape "john@example.com"
And je clique sur le champ "password"
And je tape "Password123!"
And je clique sur le bouton "Submit"
Then je vois "Welcome"
✅ Bon (déclaratif) :
Scenario: Connexion réussie
Given l'utilisateur a des identifiants valides
When l'utilisateur se connecte
Then l'utilisateur voit la page d'accueil
2. Un Scénario = Un Comportement
# ❌ Mauvais - teste plusieurs choses
Scenario: Gestion complète du panier
Given le panier est vide
When j'ajoute un produit
And je modifie la quantité
And je supprime le produit
And j'ajoute un autre produit
Then le panier contient le nouveau produit
# ✅ Bon - scénarios séparés
Scenario: Ajouter un produit au panier
Given le panier est vide
When j'ajoute un produit
Then le panier contient 1 produit
Scenario: Supprimer un produit du panier
Given le panier contient 1 produit
When je supprime le produit
Then le panier est vide
3. Éviter les Détails Techniques
# ❌ Mauvais
Scenario: Créer un utilisateur
When je fais un POST sur /api/users avec status 201
And la base de données contient un enregistrement dans la table users
# ✅ Bon
Scenario: Créer un utilisateur
When je crée un nouvel utilisateur
Then l'utilisateur est enregistré dans le système
4. Utiliser le Background Judicieusement
# ✅ Bon usage du Background
Background:
Given l'utilisateur est connecté
# ❌ Mauvais - trop spécifique
Background:
Given l'utilisateur est connecté
And le panier contient 3 articles
And l'utilisateur a une carte de crédit enregistrée
5. Nommer Clairement les Scénarios
# ❌ Mauvais
Scenario: Test 1
Scenario: Vérification
# ✅ Bon
Scenario: Connexion réussie avec des identifiants valides
Scenario: Échec de connexion avec un mot de passe incorrect
Diagramme de Processus BDD
@startuml
|Business|
start
:Définir les exigences métier;
:Écrire les scénarios Gherkin;
|Développeur|
:Implémenter les step definitions;
:Développer le code;
|Testeur|
:Exécuter les tests Cucumber;
if (Tests passent?) then (oui)
:Valider la fonctionnalité;
stop
else (non)
|Développeur|
:Corriger le code;
|Testeur|
:Ré-exécuter les tests;
endif
@enduml
Outils et Intégrations
Cucumber
- Java : Cucumber-JVM
- JavaScript : Cucumber.js
- Ruby : Cucumber
- Python : Behave
- .NET : SpecFlow
IDEs
- IntelliJ IDEA : Plugin Cucumber for Java
- VS Code : Cucumber (Gherkin) Full Support
- Eclipse : Cucumber Eclipse Plugin
CI/CD
# GitHub Actions
name: Cucumber Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run Cucumber tests
run: mvn test
- name: Publish test results
uses: EnricoMi/publish-unit-test-result-action@v1
with:
files: target/cucumber-reports/*.xml
Conclusion
Gherkin est un outil puissant pour :
- Collaboration : Langage commun entre métier et technique
- Documentation : Tests qui documentent le comportement
- Automatisation : Scénarios exécutables
- Clarté : Spécifications lisibles et compréhensibles
- Maintenabilité : Tests structurés et organisés
Utilisez Gherkin pour créer une documentation vivante qui garantit que votre application répond aux besoins métier.