Cucumber est un outil logiciel de test qui prend en charge le développement piloté par le comportement (BDD). Le BDD est une méthodologie de développement de logiciels qui met l’accent sur la collaboration entre les développeurs, les testeurs et les parties prenantes de l’entreprise.
Cucumber utilise un langage appelé Gherkin pour écrire des tests dans un langage naturel, facile à comprendre par les parties prenantes non techniques.
Cucumber prend en charge l’utilisation des données pour piloter les tests automatisés qui ont de nombreuses entrées ou avec des logiciels qui doivent être testés dans des conditions différentes. Cependant, travailler avec Cucumber s’avère difficile en raison de son manque de prise en charge native pour les données de test externalisées. Cela va à l’encontre de la philosophie originale de Cucumber, mais parfois, nous devons séparer les étapes de test des données, ce qui rend la tâche plus difficile.
Une solution possible au manque de données externalisées de Cucumber est le plug-in QAF Cucumber. Ce plug-in fonctionne avec Cucumber 5+ et offre des fonctionnalités supplémentaires, telles que la prise en charge de BDD2, des données de test à partir d’un fichier externe, entre autres.
La syntaxe BDD2 est similaire à celle de Gherkin de Cucumber, mais elle prend en charge les fichiers de données externes :
Feature: Sample
@Sample
@dataFile:${data-folder-path}/sample.csv
Scenario: Sample scenario
Given L'utilisateur obtient le token d'OpenID
And L'utilisateur accède à la page de login avec les credentials
'${username}' '${password}'
QAF Cucumber ne vient pas sans ses défauts. L’une des principales limitations que nous avons rencontrées avec Java par exemple, est son support limité pour les hooks de cycle de vie tels que @Before et @BeforeAll de Cucumber.
Certains pourraient également trouver que QAF Cucumber manque d’information en ligne, ainsi que de contenu et de présentation dans sa documentation, ce qui peut entraîner une courbe d’apprentissage abrupte et pénible.
Nous avons mentionné plus tôt que la philosophie de Cucumber va à l'encontre de la séparation des données et de la définition des étapes de test. Cependant, il est parfois nécessaire d'empêcher le partage des données de test, notamment dans un repository public, ce qui rend l'externalisation des données indispensable.
Feature: Facebook Login
Scenario Outline: User logs in with valid credentials
Given the user is on the Facebook login page
When the user enters their valid email "<email>" and valid password "<password>"
And the user clicks the login button
Then the user should be logged in successfully
Examples:
| email | password |
| test@example.com | password123 |
| test1@example.com | password123 |
Cet exemple est un fichier Feature Gherkin basique qui contient les étapes de test ainsi que les données de test. L'idée est de supprimer le contenu du bloc ‘Examples:’ et de le remplacer par le chemin du fichier de données :
Feature: Facebook Login
Scenario Outline: User logs in with valid credentials
Given the user is on the Facebook login page
When the user enters their valid email "<email>" and valid password "<passwor>"
And the user clicks the login button
Then the user should be logged in successfully
Examples:
facebook.csv
Nous pouvons ensuite parser ce fichier avant le lancement de l'application pour remplacer le chemin par les données contenues dans le fichier. De cette façon, nous pouvons utiliser Cucumber nativement avec notre framework de test de choix, sans subir les inconvénients mentionnés.
Notre implémentation pour cette solution se fera en Java, et Maven comme outil de build. Nous prendrons également comme exemple un fichier CSV, mais nous pouvons implémenter cette solution pour n'importe quelle technologie et n'importe quel format de fichier.
La première étape de notre implémentation consistera à créer un parser qui analysera nos Feature files lors de la phase de build de notre application de test. Nous pouvons y parvenir en utilisant un script qui sera exécuté avant la compilation de notre application ou nous pouvons créer un plugin Maven personnalisé qui sera intégré dans notre fichier pom.xml.
Pour cet article, nous suivrons la deuxième approche.
Créons d’abord notre classe plugin MoJo :
@Mojo(name = "cucumber-data-file", defaultPhase = LifecyclePhase.COMPILE)
public class CucumberDataFileMojo extends AbstractMojo {
public void execute() {
}
}
Maintenant que notre classe MoJo est créé, nous pouvons injecter notre objet ‘MavenProjet’ dans la classe MoJo comme suit :
@Mojo(name = "cucumber-data-file", defaultPhase = LifecyclePhase.COMPILE)
public class CucumberDataFileMojo extends AbstractMojo {
@Parameter(defaultValue = "${project}", required = true, readonly = true)
MavenProject project;
…
}
Nous pouvons maintenant commencer à implémenter la logique du parser, en commençant par récupérer tous Feature files de notre application de test :
…
@SneakyThrows
public void execute() {
File featureDirectory = new File(
project.getBuild().getTestResources().get(0).getDirectory()
+ "path/to/features"
);
if (!featureDirectory.exists() || !featureDirectory.isDirectory()) {
log.error("Feature directory not found.");
return;
}
List<File> featureFiles = getFeatureFiles(featureDirectory);
if (featureFiles.isEmpty()) {
log.error("No .feature files found in the feature directory.");
return;
}
}
…
La méthode « getFeatureFiles » peut être implémentée en fonction de la hiérarchie et de la structure du projet de test.
Les prochaines étapes constituent le cœur de notre travail : lire les données de notre fichier CSV à l'aide de la bibliothèque Java Jackson et les écrire dans le bloc Examples: du fichier de fonctionnalités correspondant.
public void execute() {
…
for (File featureFile : featureFiles) {
String originalFeatureFilePath = featureFile.getAbsolutePath();
String targetFeatureFilePath = originalFeatureFilePath
.replace(
"source/feature/file/path",
"target/feature/file/path"
);
modifyFeatureFileWithCsvData(
originalFeatureFilePath,
targetFeatureFilePath
);
}
}
Ici, nous avons appelé la méthode « modifyFeatureFileWithCsvData » pour chaque Feature file qui écrira les données CSV dans les Feature files du projet buildé. Entrons dans les détails :
…
@Parameter(defaultValue = "${project}", required = true, readonly = true)
MavenProject project;
public void modifyFeatureFileWithCsvData(
String originalFeatureFilePath,
String targetFeatureFilePath
) throws IOException, CsvException {
Map<String, List<String[]>> csvDataMap =
extractCsvDataFromFeatureFile(originalFeatureFilePath);
if (csvDataMap.isEmpty()) {
return;
}
String newFeatureContent = createModifiedFeatureContent(
originalFeatureFilePath,
csvDataMap
);
writeModifiedFeatureContent(targetFeatureFilePath, newFeatureContent);
}
…
Nous commençons par extraire les données du fichier CSV à l'aide d'un lecteur CSV de notre choix, en l'occurrence Jackson. Ensuite, nous transformons ces données au format de table de données Gherkin. Enfin, nous écrivons ce format dans le bloc Examples: du fichier Feature généré.
…
private final String examplesBlock = "Examples:";
…
private String createModifiedFeatureContent(
String featureFilePath,
Map<String, List<String[]>> csvDataMap
) throws IOException {
StringBuilder newFeatureContent = new StringBuilder();
try (BufferedReader br = new BufferedReader(new FileReader(featureFilePath))) {
String line;
while ((line = br.readLine()) != null) {
newFeatureContent.append(line).append("\n");
if (line.trim().startsWith(examplesBlock)) {
var padding = line.split(examplesBlock)[0];
String csvFileName = br.readLine().trim();
csvFileName = csvFileName
.replaceAll("\\|", "")
.trim(); // Remove pipes from the file name
// Remove spaces from the file name
csvFileName = csvFileName
.replace(" ", "");
List<String[]> csvRows = csvDataMap.get(csvFileName);
if (csvRows != null) {
String newExamplesBlock = createExamplesBlock(
csvRows,
padding.length() + 1
);
newFeatureContent.append(newExamplesBlock);
}
}
}
}
return newFeatureContent.toString();
}
…
Cette méthode lit le fichier Feature source à la recherche du bloc Examples: et remplace son contenu (le chemin du fichier de données) par les données CSV transformées à l'aide de la méthode createExamplesBlock.
…
private String createExamplesBlock(List<String[]>; csvRows, Integer paddingSize) {
return csvRows.stream()
.map(csvRow -> Arrays.stream(csvRow)
.collect(Collectors.joining(" | ", "| ", "|"))
)
.map(csvRow -> StringUtils
.leftPad(csvRow, csvRow.length() + paddingSize)
)
.collect(Collectors.joining("\n"));
}
…
Excellente avancée ! Nous avons maintenant une version basique de notre parser plugin Maven qui couvre le cas nominal d'un fichier Feature. Il reste effectivement possible d'améliorer ce plugin plus tard pour gérer toute la syntaxe Gherkin.
Pour compléter l'implémentation et rendre le plugin prêt à être utilisé dans l'application de test, il faut maintenant le builder avec Maven.
mvn clean install
Maintenant que le fichier JAR de votre plugin Maven est prêt, il est temps de l'intégrer dans l’application de test. Vous allez ajouter le plugin au fichier pom.xml sous la section des plugins de build.
<build>
...
<plugins>
...
<plugin>
<groupId>ma.neoxia</groupId>
<artifactId>cucumber-data-file-maven-plugin</artifactId>
<version>0.0.1-SNAPSHOT</version>
<executions>
<execution>
<goals>
<goal>
cucumber-data-file
</goal>
</goals>
</execution>
</executions>
</plugin>
...
</plugins>
...
</build>
Une fois que le plugin Maven est intégré et que le build est exécuté, les fichiers Feature contiendront les données extraites du fichier CSV, tout en préservant les fonctionnalités natives de Cucumber. Cela signifie que Cucumber pourra toujours interpréter et exécuter les tests comme il le ferait normalement, mais avec les données externes chargées dynamiquement.
Feature: Facebook Login
Scenario Outline: User logs in with valid credentials
Given the user is on the Facebook login page
When the user enters their valid email "<email>" and valid password "<password>"
And the user clicks the login button
Then the user should be logged in successfully
Examples:
| email | password |
| test@example.com | password123 |
| test1@example.com | password123 |
En effet, Cucumber, bien que puissant, présente des limitations lorsqu'il s'agit d'intégrer facilement des fichiers de données externes. QAF Cucumber aide à surmonter certains de ces obstacles, mais engendre aussi de nouveaux défis.
La solution détaillée, via un plugin Maven personnalisé pour intégrer les données de manière propre et flexible dans les fichiers Feature, permet de concilier simplicité et puissance sans perdre les avantages de Cucumber et de son écosystème.