Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Automatiser les tests d'acceptation : comment s...

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

Session présentée à Agile Tour Montréal 2016

Si vous rencontrez des défis dans l’automatisation de vos tests d’acceptation ou si vous vous posez des questions sur la meilleure marche à suivre, cette séance est pour vous.

Découvrez comment mettre en place une batterie de tests d’acceptation utile et pérenne en ayant du plaisir à le faire. Attention, code en vue !

Avatar for Vincent Tencé

Vincent Tencé

November 16, 2016
Tweet

More Decks by Vincent Tencé

Other Decks in Programming

Transcript

  1. Automatiser les tests d’acceptation : Comment s’y prendre ? Vincent

    Tencé @testinfected http://vtence.com http://github.com/testinfected
  2. 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
  3. 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
  4. 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 test
 assertThat(response).hasStatusCode(303); }

  5. 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();
  6. 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
  7. Manque d’abstraction DesiredCapabilities capabilities = DesiredCapabilities.firefox();
 WebDriver driver = new

    FirefoxDriver(capabilities);
 
 // Enter username and password
 driver.findElement(By.id("username")).sendKeys("Bob");
 driver.findElement(By.id("password")).sendKeys("secret");
 
 // Click login button
 driver.findElement(By.id("login")).submit();
 
 // Wait for home page to load
 WebDriverWait 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!"));
  8. 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!"
  9. 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!"));
  10. 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!"
  11. 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
  12. 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
  13. Ne cherchez pas à être exhaustif @Test
 public 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");
 }

  14. Pensez comme des utilisateurs • Rôles : Qui ? •

    Objectifs : Pour quoi ? • Activités et tâches : Quoi ? • Actions : Comment ? • Évaluations : Conséquences ?
  15. Acteurs // Plusieurs acteurs vont collaborer Actors actors = new

    Actors(config); // Un acteur initialement anonyme, avec un rôle de visiteur
 User anonymous = actors.visitor(); // Les systèmes externes aussi sont des acteurs importants
 RemoteApplication api = actors.remoteApplication();

  16. 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() { … } }
  17. 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) { … } 
 … }
  18. 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; }
 … }
  19. É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;
 }
 }
  20. Au final @Test
 public 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");
 }

  21. En incluant les acteurs externes @Test
 public 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");
 }

  22. À vous de jouer ! • Acceptez la nature asynchrone

    du Web • Écrivez des tests atomiques • Testez les parcours utilisateurs • Pensez comme des utilisateurs