Automatiser les tests d'acceptation : comment s'y prendre ?

30

Transcript of Automatiser les tests d'acceptation : comment s'y prendre ?

Automatiser les tests d’acceptation :

Comment s’y prendre ?

Vincent Tencé @testinfected

http://vtence.com http://github.com/testinfected

Terminé ?

Livraison habituelle stress inclus

Livraison souhaitée sourire inclus

Les tests clients sont essentiels à une livraison réussie

Test d’acceptation de bout en bout

Pers

isten

ce

Time

User

Int

erfa

ce

Mail

Payment

Test

Tests instables

• Échouent de façon imprévisible

• Dépendent d’un jeu unique et vivant de données de test

• Dépendent de systèmes externes hors de notre contrôle

• Gèrent mal la nature asynchrone du Web

Visez l’atomicité

• Partez d’un état initial connu minimal

• Utilisez le jeu de données minimal suffisant pour le scénario décrit

• Nettoyez avant plutôt qu’après

• Remplacez les systèmes externes par des « faux » qui sont programmables et que vous contrôlez

Utilisez vos API

public void registerUser(String email, String password) { HttpResponse response =

request.content(Form.urlEncoded() .addField("email", email) .addField("password", password) .addField("conditions", "on")) .post("/accounts");

// Pour améliorer le diagnostique du testassertThat(response).hasStatusCode(303);

}

Waits

WebDriver driver = new FirefoxDriver(); driver.get(“http://some_domain/url_that_delays_login”);

WebDriverWait wait = new WebDriverWait(driver, 2, 50);

WebElement display = wait.until(presenceOfElementLocated( By.id("some-dynamic-element"))); assertThat("display text", display.getText(), equalTo("Loaded"))

WebElement button = wait.until(elementToBeClickable( By.id(“some-button")));

button.click();

Acceptez l’asynchronisme

BrowserDriver browser = new BrowserDriver( new UnsynchronizedProber(2000, 50), new FirefoxDriver());

browser.navigate().to(“http://somedomain/url_that_delays_loading");

browser.element(By.id(“some-dynamic-element")).hasText("Loaded");

browser.element(By.id(“some-button”)).click();

java.lang.AssertionError: Tried to: check that an element by id "some-button" is enabled but: it was disabled

Manque d’abstraction

DesiredCapabilities capabilities = DesiredCapabilities.firefox();WebDriver driver = new FirefoxDriver(capabilities);// Enter username and passworddriver.findElement(By.id("username")).sendKeys("Bob");driver.findElement(By.id("password")).sendKeys("secret");// Click login buttondriver.findElement(By.id("login")).submit();// Wait for home page to loadWebDriverWait wait = new WebDriverWait(driver, 5000); wait.until(ExpectedConditions.titleIs("Home")); // Check the greeting message String greeting = driver.findElement(By.id("greeting")).getText();assertThat(greeting, equalTo("Welcome, Bob!"));

Trop de détails

Scenario: Successful login Given a user "Bob" with password "secret" And I am on the login page # Ces lignes là vont toujours ensemble And I fill in "Username" with "Bob" And I fill in "Password" with "secret" # J’ai vraiment besoin de connaître tous ces détails ? When I press "Log In" Then I should see "Welcome, Bob!"

Page Objects

DesiredCapabilities capabilities = DesiredCapabilities.firefox();WebDriver driver = new FirefoxDriver(capabilities);

// Euh, vraiment ? LogInPage loginPage = PageFactory.initElements(driver, LogInPage.class);// Voilà la partie intéressante HomePage page = loginPage.loginAs("Bob", "secret");

// Et si l’affichage est asynchrone ? assertThat(page.greetingMessage(), equalTo("Welcome, Bob!"));

Tests liés aux conditions d’acceptation

Scenario: Successful login Given the user "Bob" has registered When he logs in successfully Then he should see "Welcome, Bob!"

Tests des récits utilisateurs

• Mènent à un trop grand nombre de tests

• Créent une batterie de tests difficile à maintenir

• Diminuent la valeurs des tests d’acceptation comme source de documentation fonctionnelle

• Ne renseignent pas sur la valeur disponible aux utilisateurs

Testez les parcours utilisateurs

• Testez les interactions complètes d’un utilisateur avec le système en vue de l’atteinte d’un objectif donné

• Utilisez un petit nombre de tests de parcours utilisateurs seulement pour tester l’intégration de l’ensemble du système

Ne cherchez pas à être exhaustif

@Testpublic void joinsToGetPremiumFeaturesBySelectingAPayingPlan() { Join registration = anonymous.signUp().as(bob()); User bob = registration.selectPayingPlan("micro") .enterBillingDetails("5555555555554444",

"12/18", "999");

bob.manageAccount() .showsCurrentlyOnPlan("micro") .seesCreditCardDetails("**** **** **** 4444", "12/18");}

Pensez comme des utilisateurs

• Rôles : Qui ?

• Objectifs : Pour quoi ?

• Activités et tâches : Quoi ?

• Actions : Comment ?

• Évaluations : Conséquences ?

Acteurs

// Plusieurs acteurs vont collaborer Actors actors = new Actors(config);

// Un acteur initialement anonyme, avec un rôle de visiteurUser anonymous = actors.visitor();

// Les systèmes externes aussi sont des acteurs importantsRemoteApplication api = actors.remoteApplication();

Objectifs

// Les objectifs des utilisateurs s’expriment dans les noms des // classes de test et des scénarios de test public class JoiningTheCommunityTest {

@Test public void joinsToLearnMoreBySelectingAFreePlan() { … }

@Test public void joinsToGetPremiumFeaturesBySelectingAPayingPlan() { … } }

Activités et tâches

// Les tâches sont groupées en activités auxquelles // les acteurs participent public class Join {

public Join signUp() { … } public Join as(AccountDetails details) { screen.enterEmail(details.email) .enterPassword(details.password) .acceptConditions() .signUp(); } public User chooseFreePlan() { … } public Join selectPayingPlan(String name) { … } … }

Actions

// Les acteurs interagissent avec des éléments de l’interface // utilisateur pour accomplir leurs tâches public class SignUpScreen { public SignUpScreen enterEmail(String email) { browser.element( id("sign-up")).element(id("email")).type(email); return this;

}

public SignUpScreen enterPassword(String password) { browser.element( id("sign-up")).element(id("password")).type(password); return this;

}

… }

Évaluations

// Les interactions ont des conséquences que les acteurs // vont évaluer en posant des questions public class BillingScreen { public BillingScreen showsCurrentPlan(String planName) { browser.element(By.id("plan")) .hasText(containsStringIgnoringCase(planName)); return this; } public BillingScreen showsCurrentCardDetails(String description, String validity) { browser.element(By.id("payment")) .hasText(containsStringIgnoringCase(description)); browser.element(By.id("payment")) .hasText(containsStringIgnoringCase(validity)); return this; }}

Au final

@Testpublic void joinsToGetPremiumFeaturesBySelectingAPayingPlan() { Join registration = anonymous.signUp().as(bob()); User bob = registration.selectPayingPlan("micro") .enterBillingDetails("5555555555554444",

"12/18", "999");

bob.manageAccount() .showsCurrentlyOnPlan("micro") .seesCreditCardDetails("**** **** **** 4444", "12/18");}

En incluant les acteurs externes

@Testpublic void joinsAndSelectsAPayingPlan() throws Exception { Join registration = anonymous.signUp().as(bob());

paymentGateway.hasCreatedCustomerAccount(bob().email), "free"); mailServer.hasSentWelcomeEmailTo(bob().email); User bob = registration.selectPayingPlan("micro") .enterBillingDetails("5555555555554444",

"12/18", "999");

paymentGateway.hasCreatedCreditCard(bob().email, "5555555555554444", "12/18","999") .hasUpgradedCustomerPlan(bob().email, "micro"); bob.manageAccount() .showsCurrentlyOnPlan("micro") .seesCreditCardDetails("**** **** **** 4444", "12/18");}

À vous de jouer !

• Acceptez la nature asynchrone du Web

• Écrivez des tests atomiques

• Testez les parcours utilisateurs

• Pensez comme des utilisateurs