Sérénité BDD et scénario

Serenity BDD et Scénario

1. Vue d'ensemble

Dans cet article, nous allons jeter un coup d'œil sur le modèle de scénario dans Serenity BDD. Nous vous suggérons de lire lesbasics of Serenity BDDavant de lire celui-ci. En outre, l'article surSerenity BDD integration with Spring pourrait également être intéressant.

Le scénario, introduit dans Serenity BDD, vise à encourager de bonnes habitudes de test et des suites de tests bien conçues en permettant aux équipes de rédiger des tests plus robustes et fiables. Il est basé sur le modèle Selenium WebDriver et le modèle Objets de page. Si vous avez lu nosintroduction to Selenium, vous trouverez ces concepts plutôt familiers.

2. Dépendance Maven

Tout d'abord, ajoutons les dépendances suivantes au fichierpom.xml:


    net.serenity-bdd
    serenity-junit
    1.4.0


    net.serenity-bdd
    serenity-screenplay
    1.4.0


    net.serenity-bdd
    serenity-screenplay-webdriver
    1.4.0

Les dernières versions deserenity-screenplay etserenity-screenplay-webdriver peuvent être récupérées à partir du référentiel central Maven.

Nous avons également besoin de pilotes Web pour exécuter le scénario -ChromeDriver ouMozilla-GeckoDriver feront l'affaire. Dans cet article, nous utiliserons ChromeDriver.

La configuration de plug-in suivante est requise pour activer WebDriver, dans laquelle la valeur dewebdriver.chrome.driver doit être le chemin relatif du fichier binaire ChromeDriver dans notre projet maven:


    maven-failsafe-plugin
    2.20
    
        
            chromedriver
        
    

3. Assistance WebDriver

Nous pouvons faire en sorte que Serenity gère l'instance WebDriver en marquant l'annotation@Managed sur une variable WebDriver. Serenity ouvrira un pilote approprié au début de chaque test et le fermera à la fin du test.

Dans l'exemple suivant, nous lançons un ChromeDriver et ouvrons Google pour rechercher «exemple». Nous nous attendons à ce que le nom d'Eugen soit présent dans les résultats de recherche:

@RunWith(SerenityRunner.class)
public class GoogleSearchLiveTest {

    @Managed(driver = "chrome")
    private WebDriver browser;

    @Test
    public void whenGoogleexampleThenShouldSeeEugen() {
        browser.get("https://www.google.com/ncr");

        browser
          .findElement(By.name("q"))
          .sendKeys("example", Keys.ENTER);

        new WebDriverWait(browser, 5)https://www.example.com/serenity-screenplay
          .until(visibilityOfElementLocated(By.cssSelector("._ksh")));

        assertThat(browser
          .findElement(By.cssSelector("._ksh"))
          .getText(), containsString("Eugen (example)"));
    }
}

Si nous ne spécifions aucun paramètre pour@Managed, Serenity BDD utilisera Firefox dans ce cas. La liste complète des pilotes pris en charge par l'annotation@Managed:firefox, chrome, iexplorer, htmlunit, phantomjs.

Si nous devons tester dans IExplorer ou Edge, nous pouvons télécharger des pilotes Web à partir dehere(for IE) ethere(for Edge) respectivement. Safari WebDriver n'est disponible que sur MacOS sous/usr/bin/safaridriver.

4. Objets de page

Les objets de page Serenity représentent un objet de page WebDriver. LePageObject masque les détails de WebDriver pour une réutilisation.

4.1. Exemple de refactorisation avecPageObject

Affinons notre test précédent en utilisant d'abordPageObject en extrayant les actions de localisation d'éléments, de recherche et de vérification des résultats:

@DefaultUrl("https://www.google.com/ncr")
public class GoogleSearchPageObject extends PageObject {

    @FindBy(name = "q")
    private WebElement search;

    @FindBy(css = "._ksh")
    private WebElement result;

    public void searchFor(String keyword) {
        search.sendKeys(keyword, Keys.ENTER);
    }

    public void resultMatches(String expected) {
        assertThat(result.getText(), containsString(expected));
    }
}

WebElement représente un élément HTML. Nous pouvons interagir avec des pages Web via les API de l'interface. Dans l'exemple ci-dessus, nous avons utilisé deux méthodes pour localiser les éléments Web dans la page: par nom d'élément et par classes CSS de l'élément.

Il existe d'autres méthodes d'application lors de la recherche d'éléments Web, telles que la recherche par nom de balise, la recherche par texte de lien, etc. Reportez-vous à nosguide to Selenium pour plus de détails.

Nous pouvons également remplacerWebElement parWebElementFacade, qui fournit des API plus fluides pour gérer les éléments Web.

En tant queSerenity will automatically instantiate any PageObject fields in the JUnit test, le test précédent peut être réécrit en un test beaucoup plus propre:

@RunWith(SerenityRunner.class)
public class GoogleSearchPageObjectLiveTest {

    @Managed(driver = "chrome")
    private WebDriver browser;

    GoogleSearchPageObject googleSearch;

    @Test
    public void whenGoogleexampleThenShouldSeeEugen() {
        googleSearch.open();

        googleSearch.searchFor("example");

        googleSearch.resultMatches("Eugen (example)");
    }
}

Nous pouvons maintenant rechercher en utilisant d'autres mots-clés et faire correspondre le résultat de la recherche associée sans apporter de modifications auxGoogleSearchPageObject.

4.2. Prise en charge Async

De nos jours, de nombreuses pages Web sont servies ou rendues dynamiquement. Pour faire face à de tels cas,PageObject prend également en charge de nombreuses fonctionnalités riches qui nous permettent d'inspecter les statuts des éléments. We can check if the elements are visible, or wait until they are visible before proceeding.

Améliorons la méthoderesultMatches en nous assurant que l'élément que nous voulons voir est visible:

public void resultMatches(String expected) {
    waitFor(result).waitUntilVisible();
    assertThat(result.getText(), containsString(expected));
}

Si nous ne prévoyons pas attendre trop longtemps, nous pouvons spécifier explicitement le délai d'attente des actions en attente:

public void resultMatches(String expected) {
    withTimeoutOf(5, SECONDS)
      .waitFor(result)
      .waitUntilVisible();
    assertThat(result.getText(), containsString(expected));
}

5. Modèle de scénario

Le modèle de scénario applique les principes de conception SOLID aux tests d'acceptation automatisés. Une compréhension générale du scénario de scénario peut être expliquée dans le contextegiven_when_then comme suit:

  • given - unActor capable d'exécuter certainsTask

  • when - leActor exécute lesTask

  • then – lesActor devraient voir l'effet et vérifier les résultats

Maintenant, adaptons notre scénario de test précédent au scénario de scénario: étant donné qu'un utilisateur Kitty peut utiliser Google, lorsqu'elle recherche "exemple" sur Google, Kitty devrait voir le nom d'Eugen dans les résultats.

Commencez par définir les tâches que Kitty peut effectuer.

  1. Kitty peut utiliser Google:

    public class StartWith implements Task {
    
        public static StartWith googleSearchPage() {
            return instrumented(StartWith.class);
        }
    
        GoogleSearchPage googleSearchPage;
    
        @Step("{0} starts a google search")
        public  void performAs(T t) {
            t.attemptsTo(Open
              .browserOn()
              .the(googleSearchPage));
        }
    }
  2. Kitty peut effectuer une recherche sur Google:

    public class SearchForKeyword implements Task {
    
        @Step("{0} searches for '#keyword'")
        public  void performAs(T actor) {
            actor.attemptsTo(Enter
              .theValue(keyword)
              .into(GoogleSearchPage.SEARCH_INPUT_BOX)
              .thenHit(Keys.RETURN));
        }
    
        private String keyword;
    
        public SearchForKeyword(String keyword) {
            this.keyword = keyword;
        }
    
        public static Task of(String keyword) {
            return Instrumented
              .instanceOf(SearchForKeyword.class)
              .withProperties(keyword);
        }
    }
  3. Kitty peut voir les résultats de recherche Google:

    public class GoogleSearchResults implements Question> {
    
        public static Question> displayed() {
            return new GoogleSearchResults();
        }
    
        public List answeredBy(Actor actor) {
            return Text
              .of(GoogleSearchPage.SEARCH_RESULT_TITLES)
              .viewedBy(actor)
              .asList();
        }
    }

De plus, nous avons déjà défini la recherche GooglePageObject:

@DefaultUrl("https://www.google.com/ncr")
public class GoogleSearchPage extends PageObject {

    public static final Target SEARCH_RESULT_TITLES = Target
      .the("search results")
      .locatedBy("._ksh");

    public static final Target SEARCH_INPUT_BOX = Target
      .the("search input box")
      .locatedBy("#lst-ib");
}

Maintenant, notre classe de test principale ressemblerait à:

@RunWith(SerenityRunner.class)
public class GoogleSearchScreenplayLiveTest {

    @Managed(driver = "chrome")
    WebDriver browser;

    Actor kitty = Actor.named("kitty");

    @Before
    public void setup() {
        kitty.can(BrowseTheWeb.with(browser));
    }

    @Test
    public void whenGoogleexampleThenShouldSeeEugen() {
        givenThat(kitty).wasAbleTo(StartWith.googleSearchPage());

        when(kitty).attemptsTo(SearchForKeyword.of("example"));

        then(kitty).should(seeThat(GoogleSearchResults.displayed(),
          hasItem(containsString("Eugen (example)"))));
    }
}

Après avoir exécuté ce test, nous verrons des captures d'écran de chaque étape effectuée par Kitty dans le rapport de test:

image

6. Sommaire

Dans cet article, nous avons présenté comment utiliser Screenplay Pattern avec Serenity BDD. De plus, avec l'aide dePageObject, nous n'avons pas à interagir directement avec les WebDrivers, ce qui facilite la lecture, la maintenance et l'extension de nos tests.

Pour plus de détails surPageObject et le modèle de scénario dans Serenity BDD, consultez la section correspondante de la documentation Serenity.

Comme toujours, l'exemple de code complet peut être trouvé suron the Github.