Cucumber
Le framework de développement piloté par le comportement (BDD) qui permet d'écrire des tests d'acceptation en langage naturel, facilitant la collaboration entre les équipes techniques et non techniques.
Qu'est-ce que Cucumber ?
Imaginez que vous développez une application et que vous souhaitez vous assurer qu'elle fonctionne exactement comme prévu. Comment faire pour que tout le monde dans l'équipe - des développeurs aux responsables métier en passant par les testeurs - comprenne exactement ce qui est attendu ? C'est là qu'intervient Cucumber.
Cucumber est comme un traducteur universel entre le langage des affaires et le code informatique. Il permet d'écrire le comportement attendu de votre application en langage naturel, dans des termes que tout le monde peut comprendre - puis de transformer automatiquement ces descriptions en tests qui vérifient que votre application fonctionne correctement.
Ce que Cucumber apporte à votre projet
Communication claire
Tout le monde parle le même langage, ce qui réduit les malentendus et assure que ce qui est construit correspond vraiment à ce qui est attendu par les utilisateurs finaux.
Confiance accrue
Les tests automatiques basés sur les scénarios Cucumber vous assurent que l'application fait ce qu'elle est censée faire, même après des modifications du code.
Documentation vivante
Les scénarios Cucumber servent à la fois de spécifications et de tests, créant une documentation toujours à jour qui évolue avec votre application.
Développement agile
Parfaitement adapté aux méthodes agiles, Cucumber aide à définir clairement les critères d'acceptation et à vérifier automatiquement qu'ils sont respectés à chaque itération.
En résumé, Cucumber améliore la collaboration entre les équipes techniques et non techniques, réduit les erreurs de communication, et assure que l'application développée correspond exactement aux besoins des utilisateurs. C'est un outil précieux pour tout projet où la qualité et la conformité aux attentes des utilisateurs sont essentielles.
Fonctionnement technique
Cucumber est un framework de test BDD (Behavior-Driven Development) qui exécute des spécifications écrites en langage naturel. Il est disponible dans plusieurs langages de programmation, mais nous nous concentrerons ici sur son implémentation en
JavaScript/Node.js via le package
@cucumber/cucumber
.
Architecture et composants
Cucumber s'articule autour de trois composants principaux :
- Features (Fonctionnalités) - Fichiers .feature écrits en Gherkin qui décrivent le comportement attendu
- Step Definitions (Définitions d'étapes) - Code qui transforme les phrases en Gherkin en actions exécutables
- Support Code - Hooks, World et autres configurations qui soutiennent l'exécution des tests
Exemples pratiques avec Node.js
Fichier de feature (Gherkin)
Les fichiers .feature contiennent des scénarios écrits en Gherkin qui décrivent le comportement attendu :
# features/login.feature
Feature: Authentification utilisateur
En tant qu'utilisateur inscrit
Je veux pouvoir me connecter à l'application
Afin d'accéder aux fonctionnalités réservées aux membres
Background:
Given je suis sur la page d'accueil
Scenario: Connexion réussie
When je clique sur "Connexion"
And je remplis le champ "email" avec "utilisateur@exemple.fr"
And je remplis le champ "password" avec "motdepasse123"
And je clique sur le bouton "Se connecter"
Then je devrais voir "Bienvenue, utilisateur@exemple.fr"
And je devrais être sur la page "tableau-de-bord"
Scenario: Connexion échouée
When je clique sur "Connexion"
And je remplis le champ "email" avec "utilisateur@exemple.fr"
And je remplis le champ "password" avec "mauvais_mot_de_passe"
And je clique sur le bouton "Se connecter"
Then je devrais voir "Identifiants invalides"
And je devrais être sur la page "connexion"
Step Definitions
Les step definitions font le lien entre le langage Gherkin et le code d'automatisation :
// features/step_definitions/login_steps.js
const { Given, When, Then } = require('@cucumber/cucumber');
const { By, until } = require('selenium-webdriver');
const { expect } = require('chai');
// Hooks pour le setup et teardown
const { Before, After } = require('@cucumber/cucumber');
const { Builder } = require('selenium-webdriver');
// Initialiser le driver avant chaque scénario
Before(async function() {
this.driver = await new Builder().forBrowser('chrome').build();
await this.driver.manage().window().setRect({ width: 1280, height: 800 });
this.vars = {};
});
// Fermer le driver après chaque scénario
After(async function() {
if (this.driver) {
await this.driver.quit();
}
});
// Step definitions
Given('je suis sur la page d\'accueil', async function() {
await this.driver.get('https://example.com');
});
When('je clique sur {string}', async function(linkText) {
await this.driver.findElement(By.linkText(linkText)).click();
});
When('je remplis le champ {string} avec {string}', async function(fieldName, value) {
await this.driver.findElement(By.name(fieldName)).sendKeys(value);
});
When('je clique sur le bouton {string}', async function(buttonText) {
await this.driver.findElement(By.xpath(`//button[contains(text(), '${buttonText}')]`)).click();
});
Then('je devrais voir {string}', async function(expectedText) {
const pageSource = await this.driver.getPageSource();
expect(pageSource).to.include(expectedText);
});
Then('je devrais être sur la page {string}', async function(pagePath) {
await this.driver.wait(until.urlContains(pagePath), 5000);
const currentUrl = await this.driver.getCurrentUrl();
expect(currentUrl).to.include(pagePath);
});
Configuration de Cucumber
La configuration de Cucumber se fait via un fichier cucumber.js :
// cucumber.js
module.exports = {
default: {
parallel: 2,
format: ['html:cucumber-report.html', 'json:cucumber-report.json', 'summary'],
paths: ['features/**/*.feature'],
require: ['features/step_definitions/**/*.js', 'features/support/**/*.js'],
publishQuiet: true,
worldParameters: {
baseUrl: 'https://example.com'
},
tags: 'not @wip and not @skip'
},
// Configuration pour les tests de régression
regression: {
format: ['html:cucumber-regression-report.html'],
paths: ['features/**/*.feature'],
require: ['features/step_definitions/**/*.js', 'features/support/**/*.js'],
tags: '@regression and not @skip'
},
// Configuration pour les tests de fumée
smoke: {
format: ['html:cucumber-smoke-report.html'],
paths: ['features/**/*.feature'],
require: ['features/step_definitions/**/*.js', 'features/support/**/*.js'],
tags: '@smoke and not @skip'
}
};
// Dans package.json, vous pourriez alors avoir :
//
// "scripts": {
// "test": "cucumber-js",
// "test:regression": "cucumber-js --profile regression",
// "test:smoke": "cucumber-js --profile smoke"
// }
World et support
Le "World" est un contexte partagé entre les étapes d'un scénario :
// features/support/world.js
const { setWorldConstructor, World } = require('@cucumber/cucumber');
const { Builder } = require('selenium-webdriver');
const chrome = require('selenium-webdriver/chrome');
const firefox = require('selenium-webdriver/firefox');
const { Options } = require('selenium-webdriver/chrome');
/**
* Classe personnalisée pour le World Cucumber
* Permet de partager le contexte entre les steps d'un scénario
*/
class CustomWorld extends World {
constructor(options) {
super(options);
this.baseUrl = options.parameters.baseUrl || 'https://example.com';
this.testId = `test-${Date.now()}`;
this.screenshot = null;
}
// Méthode pour initialiser le driver avec différentes options
async buildDriver(browserName = 'chrome', headless = false) {
let driver;
if (browserName === 'chrome') {
const options = new Options();
if (headless) {
options.addArguments('--headless');
}
options.addArguments('--no-sandbox');
options.addArguments('--disable-dev-shm-usage');
options.addArguments('--window-size=1280,800');
driver = await new Builder()
.forBrowser('chrome')
.setChromeOptions(options)
.build();
}
else if (browserName === 'firefox') {
const options = new firefox.Options();
if (headless) {
options.addArguments('-headless');
}
driver = await new Builder()
.forBrowser('firefox')
.setFirefoxOptions(options)
.build();
}
else {
throw new Error(`Navigateur non supporté: ${browserName}`);
}
this.driver = driver;
return driver;
}
// Méthode pour naviguer vers une URL relative
async visit(path) {
const url = path.startsWith('http') ? path : `${this.baseUrl}${path}`;
await this.driver.get(url);
}
// Méthode pour prendre une capture d'écran
async takeScreenshot() {
if (this.driver) {
this.screenshot = await this.driver.takeScreenshot();
}
return this.screenshot;
}
}
// Enregistrer notre monde personnalisé
setWorldConstructor(CustomWorld);
Installation et démarrage rapide
Pour commencer avec Cucumber.js, installez les packages nécessaires :
npm install --save-dev @cucumber/cucumber chai selenium-webdriver @cucumber/pretty-formatter
Structure de dossiers typique d'un projet Cucumber :
features/
├── step_definitions/ # Définitions des étapes
│ ├── login_steps.js
│ └── navigation_steps.js
├── support/ # Code de support
│ ├── hooks.js # Before, After hooks
│ └── world.js # Contexte partagé
└── login.feature # Fichiers de scénarios
Fonctionnalités avancées
- Tags - Permettent de catégoriser et filtrer les scénarios (@regression, @smoke, @wip, etc.)
- Data Tables - Pour fournir des données structurées aux étapes
- Doc Strings - Pour inclure des blocs de texte plus grands
- Scenario Outlines - Pour exécuter le même scénario avec différentes données
- Hooks - Before, After, BeforeAll, AfterAll pour la configuration et le nettoyage
- Attachements - Pour enrichir les rapports avec des captures d'écran, logs, etc.
- Parameterized Step Definitions - Pour réutiliser les étapes avec des paramètres différents
Intégration avec d'autres outils
Cucumber s'intègre bien avec différents outils pour former une solution de test complète :
Selenium WebDriver - Pour les tests end-to-end d'applications web
- Puppeteer / Playwright - Alternatives modernes à Selenium pour contrôler les navigateurs
- Appium - Pour tester des applications mobiles
- REST API clients - Pour tester des
API REST
- Formatters et reporters - Pour générer des rapports HTML, JSON, JUnit, etc.
- CI/CD - Intégration dans les pipelines Jenkins,
GitHub Actions, GitLab CI, etc.
Bonnes pratiques
- Principes BRIEF - Business language, Real data, Intention revealing, Essential, Focused
- Un scénario = un comportement - Chaque scénario doit tester un seul aspect fonctionnel
- Autonomie des scénarios - Les scénarios doivent être indépendants les uns des autres
- Favoriser la réutilisation - Créer des étapes réutilisables et des helpers
- Éviter les détails d'implémentation - Se concentrer sur le comportement, pas sur la façon dont il est implémenté
- Utiliser un langage commun - Maintenir un glossaire des termes métier partagés
- Éviter les scénarios trop techniques - Les features doivent rester compréhensibles par les non-développeurs
Cucumber est un outil puissant qui, lorsqu'il est bien utilisé, peut considérablement améliorer la qualité des logiciels et la communication entre les équipes techniques et non techniques. Il encourage une approche collaborative du développement où les spécifications sont écrites ensemble et où les tests servent à la fois de documentation et de validation continue.
Cas d'usage
Spécification par l'exemple
Ateliers collaboratifs où les équipes métier, développement et qualité définissent ensemble des exemples concrets du comportement attendu, qui seront ensuite automatisés avec Cucumber.
Tests d'acceptation utilisateur
Validation que l'application répond aux critères d'acceptation définis par les utilisateurs, avec des tests exprimés dans le langage du domaine métier et automatisés pour une exécution régulière.
Documentation vivante
Création d'une documentation fonctionnelle toujours à jour, qui sert à la fois de référence pour les utilisateurs et de tests automatisés pour les développeurs, assurant que la documentation ne diverge jamais du comportement réel.
Intégration continue
Exécution automatique des tests Cucumber dans les pipelines CI/CD, permettant de vérifier que chaque changement respecte les comportements attendus et de générer des rapports détaillés sur les fonctionnalités validées.
Refactoring en confiance
Support lors de la modernisation d'applications legacy, en assurant que les comportements critiques sont bien documentés et testés avant et après les modifications majeures du code.
Tests de régression planifiés
Exécution régulière de suites de tests complètes pour vérifier que les fonctionnalités critiques continuent de fonctionner correctement à mesure que l'application évolue.
Exemple de workflow BDD avec Cucumber
Voici un workflow typique d'utilisation de Cucumber dans un contexte d'équipe agile :
- Atelier de découverte - L'équipe (PO, développeurs, testeurs) se réunit pour discuter d'une nouvelle fonctionnalité
- Rédaction collaborative des scénarios - Les exemples concrets du comportement attendu sont écrits en Gherkin
- Implémentation des step definitions - Les développeurs et testeurs automatisent les scénarios
- Les tests échouent - Les scénarios sont exécutés et échouent car la fonctionnalité n'est pas encore implémentée
- Développement de la fonctionnalité - Les développeurs codent la fonctionnalité pour satisfaire les scénarios
- Les tests passent - Les scénarios sont à nouveau exécutés et réussissent
- Intégration et tests de régression - Les scénarios sont intégrés à la suite de tests automatisés
- Documentation générée - Des rapports HTML sont générés pour documenter les fonctionnalités
Ce flux de travail cyclique garantit que les fonctionnalités développées répondent exactement aux attentes des parties prenantes, et que les comportements validés restent fonctionnels au fil du temps.