Utilisation du BDD avec Cucumber dans Squash - Automatisation des cas de test
Récupération des cas de test
Annita Administratrice Squash TM |
Pravash Product owner |
Fabrice Testeur fonctionnel |
Antonine Automaticienne |
---|---|---|---|
Configurer Squash TM | |||
Rédiger les exigences | |||
Rédiger les cas de test | |||
Transmettre les cas de test | |||
Récupérer les cas de test | |||
Automatiser les cas de test | |||
Fournir les cas de test automatisés | |||
Indiquer que l’automatisation est effectuée | |||
Exécuter les tests automatisés |
Sélection des cas de test à automatiser dans Squash TM
Antonine choisit des tests à automatiser dans l'onglet "À traiter" de l'Espace Automatisation (voir documentation Squash TM) en les sélectionnant et en cliquant sur le bouton "Me l’assigner".
Ensuite, en utilisant l'onglet "M’étant assigné" de l'Espace Automatisation (voir documentation Squash TM), elle indique que l'automatisation de ces tests est lancée :
Récupération des fichiers feature
Antonine met à jour son dépôt local pour récupérer les fichiers feature
poussés par Squash TM dans le dépôt Git distant.
git pull
Automatisation des cas de test
Annita Administratrice Squash TM |
Pravash Product owner |
Fabrice Testeur fonctionnel |
Antonine Automaticienne |
---|---|---|---|
Configurer Squash TM | |||
Rédiger les exigences | |||
Rédiger les cas de test | |||
Transmettre les cas de test | |||
Récupérer les cas de test | |||
Automatiser les cas de test | |||
Fournir les cas de test automatisés | |||
Indiquer que l’automatisation est effectuée | |||
Exécuter les tests automatisés |
Configuration du code banal (boiler plate)
Avant de mettre en œuvre les premières étapes avec Cucumber, Antonine doit configurer correctement son projet Maven.
Fichier .gitignore
Antonine crée le fichier .gitignore
habituel. Il définit les fichiers que Git ne doit pas suivre et, par conséquent, ne pas prendre en compte pour les commits, voir la documentation de Git. Ici, elle veut que Git ignore les fichiers générés par Maven.
Le fichier contient :
# ignore les fichiers créés par Maven (Java compilé, rapports de test…)
target/
pom.xml
file
Elle crée le fichier pom.xml
suivant pour utiliser Cucumber, JUnit et Selenium :
(La documentation de Maven décrit sa syntaxe et son fonctionnement.)
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>prestashoptest</groupId>
<artifactId>prestashoptest</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>7.2.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-suite</artifactId>
<version>1.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit</artifactId>
<version>7.2.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.1.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<encoding>UTF-8</encoding>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
</plugin>
</plugins>
</build>
</project>
Fichier src/test/resources/cucumber.properties
Elle crée également le fichier src/test/resources/cucumber.properties
afin d'éviter les messages de Cucumber concernant la publication des rapports.
Le fichier contient :
cucumber.publish.quiet=true
cucumber.publish.enabled=false
Configuration de Cucumber dans le fichier RunCucumberTest.java
Antonine crée le fichier de test racine src/test/java/prestashoptest/RunCucumberTest.java
:
package prestashoptest;
import org.junit.runner.RunWith;
import io.cucumber.junit.Cucumber;
@RunWith(Cucumber.class)
public class RunCucumberTest {
}
Structure de base du code
Antonine crée quelques répertoires dans src/test/java/prestashoptest
pour organiser son code. L’arborescence des fichiers qui en résulte est la suivante :
-
.git
/ → Git -
src
/-
test
/-
java
/-
prestashoptest
/datatypes
/ → types de données nécessaires pour modéliser les objets métiershelpers
/ → aides techniques (StringHelper
…)pageobjects
/ → page objectsAccountCreationPageObject.java
CartPageObject.java
CatalogPageObject.java
HomePageObject.java
IdentityPageObject.java
ProductPageObject.java
SignInPageObject.java
seleniumtools
/ → outils Selenium (aides pour l'utilisation de Selenium)stepdefinitions
/ → définition des pas CucumberAccountStepDefinitions.java
CartStepDefinitions.java
CommonStepDefinitions.java
RunCucumberTest.java
SetupTeardown.java
-
-
resources
/-
prestashoptest
/ → fichiersfeature
générés Squash TM 🛑 Ces fichiers ne doivent pas être modifiés !-
account
431_Create_an_account__then_check_login_and_personal_data.feature
432_Create_an_account__then_try_to_login_with_incorrect_password.feature
- …
-
cart
240_Add_one_product_in_the_cart.feature
303_Add_two_products_in_the_cart.feature
304_Add_twice_a_product_to_the_cart.feature
- …
-
-
cucumber.properties
→ configuration de Cucumber
-
-
-
-
.gitignore
pom.xml
Quelques notes de conception
graph TD
A[Step definitions] --> B[Page objects]
A --> C[Data types]
A --> D[Helpers]
B --> E[Selenium tools]
B --> C
B --> D
Antonine veut séparer clairement les préoccupations :
src/test/java/prestashoptest/seleniumtools
- contient la plus grande partie du code lié à l'interface avec WebDriver ;
- est une couche technique, elle ne connaît pas les règles métier.
-
src/test/java/prestashoptest/pageobjects
contient les page objects habituels.
Un page object est une classe Java qui encapsule les détails techniques d'une page testée (ID, noms, XPaths… des éléments HTML).
Ce modèle de conception présente deux avantages principaux :- Cela réduit le coût de maintenance en centralisant les détails des pages en un seul endroit : si un développeur modifie un élément HTML sur la page et que ce changement casse le localisateur utilisé par Antonine pour trouver l’élément, elle n'aura à le mettre à jour qu'à un seul endroit.
- Cela fournit une API qui a une sémantique métier : le code utilisant le page object connaît les fonctionnalités supportées par la page (par exemple, entrer email et mot de passe, puis essayer de se connecter) mais il n’a pas besoin de connaître la structure HTML ou le comportement JavaScript de celle-ci.
Les page objects utilisent WebDriver principalement via les classes
seleniumtools
(par exemple, pour toutes les opérations de base telles que la définition de la valeur d'un champ, l'obtention de la valeur d'un champ, le clic sur un élément…). Dans quelques cas, quand un page object doit effectuer des opérations qui sont trop spécifiques pour être dans une classeseleniumtools
, il appelle WebDriver directement. -
src/test/java/prestashoptest/stepdefinitions
contient l'implémentation des étapes Cucumber.
Chaque étape Cucumber interagit avec certains page objects. Elle n'a aucune connaissance du WebDriver, de la structure HTML, du code JavaScript…
Le répertoire src/test/resources/prestashoptest
🛑 Le contenu du répertoire src/test/resources/prestashoptest
ne doit pas être modifié, il est contrôlé par Squash TM.
La modification de certains fichiers de ce répertoire pourrait entraver la transmission de nouveaux cas de test ou de cas de test mis à jour (c'est-à-dire les fichiers feature
) à l'avenir.
Mais, même si la transmission fonctionnait, toutes les modifications seraient perdues, écrasées par Squash TM.
Écriture des outils Selenium
Antonine utilise l'héritage pour faciliter les appels Selenium : src/test/java/prestashoptest/seleniumtools/PageObjectBase.java
est la classe de base pour tous les page objects.
Antonine y place des méthodes utilitaires basiques pour
-
remplir un champ et récupérer sa valeur
protected void fillFieldValue(final SelectBy by, final String fieldKey, final String value) { final WebElement element = by.findElement(getDriver(), fieldKey); element.clear(); element.sendKeys(value); } protected String getFieldValue(final SelectBy by, final String fieldKey) { return by.findElement(getDriver(), fieldKey).getAttribute("value"); }
-
cliquer sur un élément
protected void clickElement(final SelectBy by, final String elementKey) { by.findElement(getDriver(), fieldKey).click(); }
-
obtenir l'état d'une case à cocher
protected boolean isCheckBoxSelected(final SelectBy by, final String checkBoxKey) { return by.findElement(getDriver(), checkBoxKey).isSelected(); }
-
et ainsi de suite…
Elle contient également quelques méthodes statiques pour déclarer l'hôte où le SUT est déployé, pour sélectionner le navigateur à utiliser pour le test, pour générer une capture d'écran, pour quitter la session WebDriver… (Ces méthodes sont principalement utilisées lors du setup et du teardown des tests décrits ci-dessous).
public static void setHost(final String host) {
PageObjectBase.host = host;
}
public static void setBrowser(final Browser browser) {
PageObjectBase.browser = browser;
}
public static void quit() {
PageObjectBase.browser.quit();
}
public static byte[] getScreenshot() {
return ((TakesScreenshot)getDriver()).getScreenshotAs(OutputType.BYTES);
}
Écriture du setup et du teardown
Antonine implémente ensuite le setup et le teardown des tests qu'elle place dans src/test/java/prestashoptest/SetupTeardown.java
.
-
Le setup des tests :
Ce setup initialise le type de navigateur et l'URL de l'application testée.@Before public void setup() { PageObjectBase.setBrowser(PageObjectBase.Browser.FIREFOX); PageObjectBase.setHost("http://localhost:8080/fr"); }
-
Le teardown des tests :
Ce teardown génère une capture d'écran et la transmet à Cucumber en cas d'échec du test. Cela sera très utile pour analyser la raison de l'échec.@After public void closeBrowser(final Scenario scenario) { if (scenario.isFailed()) { final byte[] screenshot = PageObjectBase.getScreenshot(); final String screenshotName = "screenshot " + scenario.getName() + " (line " + scenario.getLine() + ")"; scenario.attach(screenshot,"image/png", screenshotName); } PageObjectBase.quit(); }
Le teardown permet également de quitter la session WebDriver.
Écriture des page objects
Une fois ce code de base écrit, Antonine crée un page object pour chaque page avec laquelle un pas de test Cucumber va interagir.
La plupart des page objects sont très simples et n'utilisent que les méthodes de la classe PageObjectBase
.
Par exemple, pour la page de création de compte :
le page object (fichier src/test/java/prestashoptest/pageobjects/AccountCreationPageObject.java
) est :
package prestashoptest.pageobjects;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import prestashoptest.datatypes.Gender;
import prestashoptest.seleniumtools.PageObjectBase;
/**
* Account creation page
*/
public class AccountCreationPageObject extends PageObjectBase {
public AccountCreationPageObject() {
super("connexion?create_account=");
}
/**
* Fill the form with the specified values (default values are used for the other fields)
* and initiate the account creation.
*
* @param gender
* @param firstName
* @param lastName
* @param password
* @param email
* @param birthDate
* @param acceptPartnerOffers
* @param acceptPrivacyPolicy
* @param acceptNewsletter
* @param acceptGdpr
*/
public void fillNewAccountFields(final Gender gender,
final String firstName,
final String lastName,
final String password,
final String email,
final LocalDate birthDate,
final boolean acceptPartnerOffers,
final boolean acceptPrivacyPolicy,
final boolean acceptNewsletter,
final boolean acceptGdpr) {
fillGender(gender);
fillFirstName(firstName);
fillLastName(lastName);
fillEmail(email);
fillPassword(password);
fillBirthDate(birthDate);
if (acceptPartnerOffers) acceptPartnerOffers();
if (acceptPrivacyPolicy) acceptPrivacyPolicy();
if (acceptNewsletter) acceptNewsletter();
if (acceptGdpr) acceptGdpr();
submitNewAccountForm();
}
/**
* Fill the gender field
* If the gender is undefined, the field is untouched.
*
* @param gender
*/
public void fillGender(final Gender gender) {
if (gender.equals(Gender.UNDEFINED)) {
return;
}
final String id = (gender.equals(Gender.MALE)) ? "field-id_gender-1"
: "field-id_gender-2";
clickElement(SelectBy.ID, id);
}
/**
* Fill the first name field
*
* @param firstName
*/
public void fillFirstName(final String firstName) {
fillFieldValue(SelectBy.ID, "field-firstname", firstName);
}
/**
* Fill the last name field
*
* @param lastName
*/
public void fillLastName(final String lastName) {
fillFieldValue(SelectBy.ID, "field-lastname", lastName);
}
/**
* Fill the email field
*
* @param email
*/
public void fillEmail(final String email) {
fillFieldValue(SelectBy.ID, "field-email", email);
}
/**
* Fill the password field
*
* @param password
*/
public void fillPassword(final String password) {
fillFieldValue(SelectBy.ID, "field-password", password);
}
/**
* Fill the password field
*
* @param birthDate
*/
public void fillBirthDate(final LocalDate birthDate) {
fillFieldValue(SelectBy.ID, "field-birthday", birthDate.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")));
}
/**
* Approve for partner offers
*/
public void acceptPartnerOffers() {
clickElement(SelectBy.NAME, "optin");
}
/**
* Approve the customer privacy policy
*/
public void acceptPrivacyPolicy() {
clickElement(SelectBy.NAME, "customer_privacy");
}
/**
* Sign up for the newsletter
*/
public void acceptNewsletter() {
clickElement(SelectBy.NAME, "newsletter");
}
/**
* Approve the GDPR policy
*/
public void acceptGdpr() {
clickElement(SelectBy.NAME, "psgdpr");
}
/**
* Initiate the account creation
*/
private void submitNewAccountForm() {
clickElement(SelectBy.XPATH, "//*[@id=\"customer-form\"]/footer/button");
}
}
Dans certains cas, il est plus difficile de naviguer dans l'HTML, donc le page object est plus complexe.
Par exemple, pour récupérer le contenu du panier :
le page object (fichier src/test/java/prestashoptest/pageobjects/CartPageObject.java
) est :
package prestashoptest.pageobjects;
import java.util.ArrayList;
import java.util.List;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import prestashoptest.datatypes.CartLine;
import prestashoptest.seleniumtools.PageObjectBase;
public class CartPageObject extends PageObjectBase {
public CartPageObject() {
super("panier?action=show");
}
public List<CartLine> getContent() {
final List<WebElement> items = getElements(SelectBy.CLASS, "cart-item");
final List<CartLine> list = new ArrayList<CartLine>(items.size());
for (final WebElement item: items) {
final WebElement productElem = item.findElement(By.className("label"));
final String product = productElem.getText();
final WebElement quantityElem = item.findElement(By.name("product-quantity-spin"));
final int quantity = Integer.parseInt(quantityElem.getAttribute("value"));
final List<WebElement> dimensionElem = item.findElements(By.className("dimension"));
final String dimension = dimensionElem.isEmpty() ? null : dimensionElem.get(0).findElement(By.className("value")).getText();
final List<WebElement> sizeElem = item.findElements(By.className("taille"));
final String size = sizeElem.isEmpty() ? null : sizeElem.get(0).findElement(By.className("value")).getText();
final List<WebElement> colorElem = item.findElements(By.className("couleur"));
final String color = colorElem.isEmpty() ? null : colorElem.get(0).findElement(By.className("value")).getText();
list.add(new CartLine(product, quantity, dimension, size, color));
}
return list;
}
}
Implémentation des pas de test Cucumber
Les fichiers src/test/java/prestashoptest/stepdefinitions/*.java
contiennent les définitions des pas de test Cucumber.
Antonine regroupe les pas de test liés à un domaine de fonctionnalités donné dans une même classe. Elle crée donc AccountStepDefinitions.java
, CartStepDefinitions.java
… Enfin, CommonStepDefinitions.java
contient des pas de test transverses.
Il y a trois types de pas :
Given
Un pas Given
est utilisé pour établir une précondition de test.
Il peut naviguer et interagir avec plusieurs pages afin de préparer les données nécessaires. Il peut même configurer les données en contournant l'interface utilisateur de l'application : en appelant l'API REST fournie par l'application, en injectant des données dans la base de données de l'application…
Un pas Given
n'a pas besoin d'être sur une page particulière pour être appelé.
Par exemple, certains cas de test ont besoin que l’utilisateur (non spécifique) soit connecté. Donc ils utilisent le pas Given I am logged in
.
Ce pas est implémenté en utilisant la page de création de compte et en créant un utilisateur temporaire (fichier src/test/java/prestashoptest/stepdefinitions/AccountStepDefinitions.java
) :
package prestashoptest.stepdefinitions;
…
import io.cucumber.java.en.Given;
…
public class AccountStepDefinitions {
/**
* Create a temporary user and keep him/her logged in
*/
@Given("I am logged in")
public void createTemporaryUser() {
final String id = StringsHelper.generateRandomId();
final AccountCreationPageObject newAccountPage = new AccountCreationPageObject();
newAccountPage.goTo();
newAccountPage.fillNewAccountFields(Gender.UNDEFINED,
"first. " + id,
"last. " + id,
id,
id + "@example.com",
LocalDate.of(2000, 1, 1),
true,
true,
true,
true);
final HomePageObject homePage = new HomePageObject();
homePage.assertIsCurrent();
}
…
}
When
Un pas When
exécute une action.
Par example, When I am on the AccountCreation page
est implémenté (dans le même fichier) par :
/**
* Go to the Account Creation page
*/
@When("I am on the AccountCreation page")
public void displayNewAccountPage() {
final AccountCreationPageObject newAccountPage = new AccountCreationPageObject();
newAccountPage.goTo();
}
De nombreux pas de test When
nécessitent d'être exécutés alors qu'une page donnée est affichée, ils vérifient donc que c'est bien le cas.
Par exemple, l'implémentation du pas When("I fill AccountCreation fields with gender {string} firstName {string} lastName {string} password {string} email {string} birthDate {string} acceptPartnerOffers {string} acceptPrivacyPolicy {string} acceptNewsletter {string} acceptGdpr {string} and submit
est :
/**
* Fill the fields of the Account Creation page and submit the form
*
* The current page must be the Account Creation page
*/
@When("I fill AccountCreation fields with gender {string} firstName {string} lastName {string} password {string} " +
"email {string} birthDate {string} acceptPartnerOffers {string} acceptPrivacyPolicy {string} acceptNewsletter{string} acceptGdpr {string} and submit")
public void fillAccountCreationFieldsAndSubmit(final String genderCode,
final String firstName,
final String lastName,
final String password,
final String email,
final String birthDate,
final String acceptPartnerOffers,
final String acceptPrivacyPolicy,
final String acceptNewsletter,
final String acceptGdpr) {
final AccountCreationPageObject newAccountPage = new AccountCreationPageObject();
newAccountPage.assertIsCurrent();
newAccountPage.fillNewAccountFields(Gender.ofCode(genderCode),
firstName,
lastName,
password,
email,
LocalDate.parse(birthDate),
StringsHelper.convertYesNoIntoBoolean(acceptPartnerOffers),
StringsHelper.convertYesNoIntoBoolean(acceptPrivacyPolicy),
StringsHelper.convertYesNoIntoBoolean(acceptNewsletter),
StringsHelper.convertYesNoIntoBoolean(acceptGdpr));
}
Then
Un pas Then
est utilisé pour vérifier qu'une postcondition de test est remplie.
Il peut naviguer et interagir avec plusieurs pages afin de vérifier que les données sont conformes aux attentes. Il peut même récupérer les données en contournant l'interface utilisateur de l'application : en appelant l'API REST fournie par l'application, en interrogeant la base de données de l'application…
Un pas Then
n'a pas besoin d'être sur une page particulière pour être appelé.
Par exemple, l'implémentation du pas Then My personal information is gender {string} firstName {string} lastName {string} email {string} birthDate {string} acceptPartnerOffers {string} acceptPrivacyPolicy {string} acceptNewsletter {string} acceptGdpr {string}
est réalisée en affichant la page des données personnelles, en récupérant les valeurs des champs et en vérifiant qu'elles ont les valeurs attendues :
/**
* Verify that the personal data is equal to the given parameters
*/
@Then("My personal information is gender {string} firstName {string} lastName {string} " +
"email {string} birthDate {string} acceptPartnerOffers {string} acceptPrivacyPolicy {string} acceptNewsletter {string} acceptGdpr {string}")
public void assertPersonalInformation(final String genderCode,
final String firstName,
final String lastName,
final String email,
final String birthDate,
final String acceptPartnerOffers,
final String acceptPrivacyPolicy,
final String acceptNewsletter,
final String acceptGdpr) {
final IdentityPageObject identityPage = new IdentityPageObject();
identityPage.goTo();
Assertions.assertEquals(Gender.ofCode(genderCode),
identityPage.getGender(),
"The effective gender is not the expected one");
Assertions.assertEquals(firstName,
identityPage.getFirstName(),
"The effective first name is not the expected one");
Assertions.assertEquals(lastName,
identityPage.getLastName(),
"The effective last name is not the expected one");
Assertions.assertEquals(email,
identityPage.getEmail(),
"The effective email is not the expected one");
Assertions.assertEquals(LocalDate.parse(birthDate),
identityPage.getBirthDate(),
"The effective birth date is not the expected one");
Assertions.assertEquals(StringsHelper.convertYesNoIntoBoolean(acceptPartnerOffers),
identityPage.doesAcceptPartnerOffers(),
"The effective acceptPartnerOffers is not the expected one");
Assertions.assertEquals(StringsHelper.convertYesNoIntoBoolean(acceptPrivacyPolicy),
identityPage.doesAcceptPrivacyPolicy(),
"The effective acceptPrivacyPolicy is not the expected one");
Assertions.assertEquals(StringsHelper.convertYesNoIntoBoolean(acceptNewsletter),
identityPage.doesAcceptNewsletter(),
"The effective acceptNewsletter is not the expected one");
Assertions.assertEquals(StringsHelper.convertYesNoIntoBoolean(acceptGdpr),
identityPage.doesAcceptGdpr(),
"The effective acceptGdpr is not the expected one");
}
Quelques détails d'implémentation
Expressions régulières et expressions Cucumber
Il existe deux façons d'écrire les patterns correspondant aux paramètres des pas Gherkin :
-
les expressions régulières Java usuelles :
public class CartStepDefinitions { @Then("I have (\\d+) products in my cart") public void assertNumberOfProductsInCart(final String argNbProducts) { int nbProducts = Integer.parseInt(argNbProducts); System.out.format("Products: %n\n", nbProducts); } }
-
les expressions Cucumber :
Elles permettent de faire correspondre rapidement les données usuelles (string, word, int, float…) sans avoir à écrire le code boiler pour convertir le texte en nombres entiers, nombres décimaux… donc Antonine utilise celles-ci.public class CartStepDefinitions { @Then("I have {int} products in my cart") public void assertNumberOfProductsInCart(final int nbProducts) { System.out.format("Products: %n\n", nbProducts); } }
Lambdas vs. méthodes classiques
Il y a deux façons d'implémenter les pas Gherkin :
-
en utilisant des méthodes classiques :
Pour cela, la dépendance suivante doit être déclarée dans le fichierpackage prestashoptest.stepdefinitions; import io.cucumber.java.en.Then; public class CartStepDefinitions { @Then("I have {int} products in my cart") public void assertNumberOfProductsInCart(final int nbProducts) { System.out.format("Products: %n\n", nbProducts); } }
pom.xml
:<dependency> <groupId>io.cucumber</groupId> <artifactId>cucumber-java</artifactId> <version>7.2.3</version> <scope>test</scope> </dependency>
-
en utilisant des lambdas :
Pour cela, la dépendance suivante doit être ajoutée dans le fichierpackage prestashoptest.stepdefinitions; import io.cucumber.java8.En; public class CartStepDefinitions implements En { public CartStepDefinitions() { Then("I have {int} products in my cart", (final Integer nbProducts) -> { System.out.format("Products: %n\n", nbProducts); }); } }
pom.xml
:<dependency> <groupId>io.cucumber</groupId> <artifactId>cucumber-java8</artifactId> <version>7.2.3</version> <scope>test</scope> </dependency>
La deuxième méthode nécessite moins de code, il n'est pas nécessaire de définir le nom de méthode inutile assertNumberOfProductsInCart
. Mais Cucumber ne supporte les méthodes lambdas que jusqu'à 9 paramètres dans un pas Gherkin donnée.
Fabrice et Antonine se sont mis d'accord pour avoir un seul pas pour remplir un formulaire de demande donné afin de garder les pas simples pour Fabrice. Mais le formulaire de création de compte a 10 paramètres, donc Antonine s'en tient aux méthodes classiques.
Utiliser Maven pour générer le squelette de code
Afin de gagner du temps pour l'implémentation des pas Gherkin, Antonine utilise Maven pour générer le squelette de code. Elle lance le test :
mvn clean test -Dcucumber.features=src/test/resources/prestashoptest/Account_management/431_Create_an_account__then_check_login_and_personal_data.feature
[ERROR] The step 'I fill AccountCreation fields with gender "M" firstName "John" lastName "Doe" password "mypassword" email "jdoe@example.com" birthDate "1990-01-02" acceptPartnerOffers "no" acceptPrivacyPolicy "yes" acceptNewsletter "yes" acceptGdpr "yes" and submit' is undefined.
You can implement this step using the snippet(s) below:
@When("I fill AccountCreation fields with gender {string} firstName {string} lastName {string} password {string} email {string} birthDate {string} acceptPartnerOffers {string} acceptPrivacyPolicy {string} acceptNewsletter {string} acceptGdpr {string} and submit")
public void i_fill_account_creation_fields_with_gender_first_name_last_name_password_email_birth_date_accept_partner_offers_accept_privacy_policy_accept_newsletter_accept_gdpr_and_submit(String string, String string2, String string3, String string4, String string5, String string6, String string7, String string8, String string9, String string10) {
// Write code here that turns the phrase above into concrete actions
throw new io.cucumber.java.PendingException();
}
cucumber-java8
est utilisé, les squelettes proposés utilisent des lambdas).
Livraison des cas de test automatisés
Annita Administratrice Squash TM |
Pravash Product owner |
Fabrice Testeur fonctionnel |
Antonine Automaticienne |
---|---|---|---|
Configurer Squash TM | |||
Rédiger les exigences | |||
Rédiger les cas de test | |||
Transmettre les cas de test | |||
Récupérer les cas de test | |||
Automatiser les cas de test | |||
Fournir les cas de test automatisés | |||
Indiquer que l’automatisation est effectuée | |||
Exécuter les tests automatisés |
Une fois qu'Antonine a implémenté tous les pas Cucumber utilisés par les fichiers features
, elle vérifie que les tests fonctionnent correctement en les exécutant :
mvn test -Dcucumber.plugin="html:target/cucumber-reports/Cucumber_html.html"
target/cucumber-reports/Cucumber_html.html
) :
Lorsqu'elle trouve quelque chose de suspect lors de l'automatisation d'un test, elle effectue le test manuellement et, s'il y a vraiment un bug, elle le signale comme décrit dans la documentation Squash TM.
Lorsque les tests se déroulent correctement, elle commite et pousse :
git add .
git commit -m "Implemented first account and cart steps"
git push
Indication que l’automatisation a été effectuée
Annita Administratrice Squash TM |
Pravash Product owner |
Fabrice Testeur fonctionnel |
Antonine Automaticienne |
---|---|---|---|
Configurer Squash TM | |||
Rédiger les exigences | |||
Rédiger les cas de test | |||
Transmettre les cas de test | |||
Récupérer les cas de test | |||
Automatiser les cas de test | |||
Fournir les cas de test automatisés | |||
Indiquer que l’automatisation est effectuée | |||
Exécuter les tests automatisés |
Ensuite, à l'aide de l'onglet "M’étant assigné" de l’Espace Automatisation (voir la documentation Squash TM), Antonine indique que les tests ont été automatisés :
Elle définit également la technologie des tests : (Ce paramètre devrait être automatiquement rempli par Squash TM, cela sera corrigé dans une prochaine version).