Les xPath et les tableaux : 8 exercices pour se perfectionner

On ne le dira jamais assez, rien ne surpasse les id dans les techniques d’identification des objets dans un DOM. C’est le plus simple et aussi le plus rapide à trouver par Selenium. Mais parfois (souvent) on ne peut pas faire autrement que d’écrire des xPath tellement complexes qu’ils frisent le write-only. Ces moments de solitude arrivent souvent lorsqu’on interagit avec des tableaux. On met 10 minutes à trouver la bonne formule et deux semaines après on ne s’en souvient plus. Nous voulons que ce temps soit révolu !

Voici donc 8 défis pour vous entraîner à écrire des xPath pour attraper des éléments dans des tableaux. Pour vérifier vos réponses nous vous conseillons l’outil CSS and XPath checker.

Les réponses se trouvent en bas de l’article.

xPath vs tableau HTML

Dans cet exercice, nous nous baserons sur le tableau suivant (vous pourrez valider vous-mêmes les formules sur la présente page) :

Fruit Description Prix au kilo (XPF)
Pomme-liane Un feu d’artifice de saveurs acidulées 700
Banane poingo Le compromis entre la patate et la banane 500
Pomme-cannelle De loin, ressemble à un artichaut 630
Letchi Ce n’est plus la saison 700
Noix de coco Son germe est délicieux 350
Chouchoute Oups, ce n’est pas un fruit 300

 

Sous chaque question se trouve une copie d’écran du tableau avec le résultat attendu en surbrillance.

Exercices de xPath pour identifier des éléments dans le tableau

1) Ecrire un xPath pour récupérer la deuxième cellule qui contient « Pomme ».

2) Ecrire un xPath pour récupérer la première case des lignes dont la colonne 3 contient la chaîne 700.

3) Ecrire un xPath pour récupérer la deuxième case après les cellules contenant « Pomme-cannelle ».

4) Ecrire un xPath pour récupérer les cases qui contiennent des prix entre 300 (inclus) et 630 (exclu).

5) En hommage à Georges Perec, écrire un xPath pour récupérer les cases qui ne contiennent pas de « e » minuscule (cases de l’entête du tableau comprises).

6) Ecrire un xPath pour récupérer les lignes qui contiennent une case dont le texte commence par « Oups ».

7) Ecrire un xPath pour récupérer les cellules du tableau (hors entête) qui contiennent la chaîne « de », qu’elle soit en minuscules ou en majuscules.

8) Allez, un bien compliqué pour finir. Ecrire un xPath pour récupérer les noms des fruits dont la description contient le texte « est » et dont le prix est entre 300 (inclus) et 630 (exclu).

Alors, combien d’exercices avez-vous réussi à faire ? 😀

Rappel de bonnes pratiques

Attention, même s’il est préférable d’avoir des sélecteurs simples à comprendre, ne faites pas reposer la lisibilité de votre projet d’automatisation sur la lisibilité de vos sélecteurs. Envisagez l’adoption de l’objet Selecteur tel que nous détaillions dans notre article sur la lisibilité des logs Selenium :

public class Selecteur {
   public String nom;
   public By chemin;

   public Selecteur(String nom, By chemin){
      this.nom = nom;
      this.chemin = chemin;
   }

   public String getNom(){
      return nom;
   }

   public By getChemin(){
      return chemin;
   }
}

Avec comme exemple d’application :

protected void clickElementTableau(int ligne, int colonne) {
    Selecteur cellule = new Selecteur("la cellule située sur la ligne " + ligne + " et la colonne " + colonne + " du tableau", By.xpath("//tr[" + ligne + "]/td[" + colonne + "]"));
    logger.info("Clic sur " + cellule.getNom());
    driver.findElement(cellule.getChemin()).click();
}

En sortie, vous aurez des logs en langage naturel, par exemple : « Clic sur la cellule située sur la ligne 3 et la colonne 4 du tableau ».

Rappels sur les xPath

  • les xPath sont en base 1, c’est-à-dire que le premier élément d’une série a comme index 1, contrairement, par exemple, aux tableaux et aux listes Java dont le premier élément a pour index 0.
  • les xPath sont sensibles à la casse quand on fait une recherche sur du texte ou un attribut

Solutions

Il existe souvent plusieurs solutions possibles, celles ci-dessous ne sont que des possibilités.

1. Un xPath pour récupérer la deuxième cellule qui contient « Pomme » :

(//td[contains(., 'Pomme')])[2]

2. Un xPath pour récupérer la première case des lignes dont la colonne 3 contient la chaîne 700 :

//td[3][contains(., '700')]//../td[1]

3. Un xPath pour récupérer la deuxième case après la cellule qui contient « Pomme-cannelle » :

//td[contains(., 'Pomme-cannelle')]/following-sibling::td[2]

4. Un xPath pour récupérer les cases qui contiennent des prix entre 300 (inclus) et 630 (exclu) :

//td[. >= 300 and 630 > .]

5. Un xPath pour récupérer les cases qui ne contiennent pas de « e » minuscule (cases de l’entête du tableau comprises) :

//td[not(contains(., 'e'))] | //th[not(contains(., 'e'))]

6. Un xPath pour récupérer les lignes qui contiennent une case dont le texte commence par « Oups » :

//td[starts-with(., 'Oups')]/parent::tr

7. Un xPath pour récupérer les cellules du tableau (hors entête) qui contiennent la chaîne « de », qu’elle soit en minuscules ou en majuscules :

//td[contains(translate(., 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), 'de')]

Conseil : si possible encapsuler cette transformation de casse dans une fonction.

8. Un xPath pour récupérer les noms des fruits dont la description contient le texte « est » et dont le prix est entre 300 (inclus) et 630 (exclu).

//td[. >= 300 and 630 > .]/preceding-sibling::td[contains(., 'est')]/preceding-sibling::td

Même exercice pour les sélecteurs CSS

Mise à jour du 24 juin 2019 : nous avons découvert, via un utilisateur du Slack de la Test Automation University, un exercice en ligne pour s’entraîner à écrire des sélecteurs CSS. Un petit jeu interactif réjouissant et très bien fait : découvrez CSS Diner !

Tests autos d’applis lourdes : le défi du gratuit

Vous venez du monde de Selenium, vous aimez ce framework gratuit, sa souplesse, la beauté du pattern page-object et son DSL aux petits oignons. Magnifique, mais aujourd’hui vous devez automatiser les tests d’une application lourde.

Il y a quelques années encore, choisir un outil d’automatisation gratuit et open-source pour ce genre de tests était un sérieux défi et présentait de nombreux risques : frameworks peu maintenus, résultats instables… Une solution propriétaire telle qu’UFT pouvait alors s’imposer comme un choix par défaut.

Est-ce différent aujourd’hui ? Nous pensons que certaines solutions sont désormais assez mûres pour être choisies au sein de projets de test ambitieux.

Pywinauto : go !

Ce nom incongru est la contraction de Python, Windows et Automation. Ce framework de test a vu le jour en 2006 (!), et donne lieu à des releases régulières depuis 2015. La version 1.0 n’est pas encore sortie (modestie de la part de Vasily Ryabov, développeur de Pywinauto ?) mais à l’utilisation, l’outil est parfaitement stable.

Avantages de Pywinauto

Utilisable dans tout IDE Python, la solution permet à l’utilisateur d’architecturer les projets de tests comme bon lui semble. Nous avons opté pour le pattern page-object, un grand classique qui permet de maintenir facilement les tests.

Principes

Identification des objets pour Pywinauto

L’identification des objets d’application Windows peut se faire grâce à des outils comme Inspect.exe, fourni avec le SDK Windows. UI Spy peut aussi être envisagé, mais il n’est plus maintenu par Microsoft. Cependant, à l’usage, nous l’avons trouvé plus rapide.

Grâce à ces utilitaires, il est possible de connaître les paramètres des objets, et donc de les identifier pour interagir avec eux (voir la liste des paramètres gérés par Pywinauto).

Interaction avec les objets avec Pywinauto

Dans l’exemple ci-dessous, nous déclarons ainsi une page ainsi que quelques objets, en utilisant au besoin plusieurs paramètres.

class AccueilPage:

    def __init__(self, test):
        self.ECRAN_ACCUEIL = test.app.window(title='Ma super appli - Accueil')
        self.INPUT_NOM = self.ECRAN_ACCUEIL.child_window(control_type="Edit", auto_id='23', found_index=0)
        self.INPUT_PRENOM = self.ECRAN_ACCUEIL.child_window(control_type="Edit", auto_id='24', found_index=0)

Dans l’architecture que nous avons mise en place, un test doit être invoqué au moment de l’appel du constructeur de la page. Ce test est initialisé au début de chaque scénario et correspond à la classe suivante :

class Test:
    app = Application(backend='uia')

    def __init__(self):
        parser = argparse.ArgumentParser()
        parser.add_argument("--log", help="enable logging", type=str, required=False)
        args = parser.parse_args()

        actionlogger.enable()
        logger = logging.getLogger('pywinauto')
        if args.log:
            logger.handlers[0] = logging.FileHandler(args.log)

        self.app = Application(backend='uia').start(r'C:cheminversmasuperappli.EXE')

Selon la façon dont l’application a été développée, certains objets ont un titre qui est affiché dans l’arborescence, ici d’UI Spy :

Dans cet exemple, on peut interagir avec le bouton « Appliquer » en utilisant le mot-clé title :

test.app.window(title='Appliquer')

Points d’attention

Certains objets complexes, par exemple des cellules de tableaux spécifiques, n’apparaissent pas toujours dans l’arborescence. Ce n’est pas nécessairement un problème provenant de Pywinauto, mais il faut garder en tête des solutions de contournement, par exemple la reconnaissance d’image que l’on trouve entre autres avec Sikulix.

Verdict

De notre côté, c’est un go ! Pywinauto a toute sa place au sein d’un POC, et a en outre l’avantage d’avoir un support efficace. Vasily Ryabov est très réactif sur Github aussi bien que sur StackOverflow, ce qui permet d’aller au-delà des blocages éventuels. EDIT du 19/20/2020 : et c’est toujours vrai en 2020 !

Winium : no go !

Framework basé sur Selenium et adapté aux applications Windows, la promesse de Winium est (était) séduisante… Malheureusement, il n’est plus maintenu et certains bugs empêchent de s’en servir efficacement. Nous vous expliquons cependant ce qui nous a plu et déplu dans cet outil, qui reste une excellente initiative.

Avantages de Winium

Pour un habitué de Selenium, la transition se fait en douceur ; les mêmes méthodes peuvent être utilisées dans la plupart des cas.

Vous ne rêvez pas ! On dirait bien un test Selenium…

Problèmes rencontrés

Identification des éléments

Afin d’interagir avec les objets de l’application à tester, plusieurs méthodes sont possibles (mais moins que pour Pywinauto). De la plus à la moins robuste, on peut procéder :

  • Par Id
  • Par Name
  • Par ClassName
  • Par Xpath

Les trois premiers paramètres, que l’on peut trouver avec les outils mentionnés précédemment, sont les plus pratiques à utiliser. Malheureusement, ils ne sont pas toujours renseignés. Il faut  alors avoir recours à la recherche par xPath, ce qui peut être plus difficile dans ce contexte que dans celui d’un DOM HTML.

Quelle que soit la méthode utilisée, les éléments semblent être trouvés d’autant plus lentement que l’interface compte un grand nombre d’objets.

Comme solution de contournement pour accélérer le test, nous avons utilisé Sikulix, avec toutes les questions que cela soulève côté maintenance.

Bug de focus

Nous avons relevé un bug assez ennuyeux : il arrive que l’application cible ne reçoive pas le focus pendant les tests. Pour peu que la fenêtre de l’IDE soit affichée, on se retrouve à taper l’identifiant en lieu et place du script de test.

A part fermer les fenêtres inutiles et réduire la fenêtre de l’IDE après le lancement de la campagne, nous n’avons pas trouvé de solution à ce problème.

Des nouvelles du créateur de Winium

Les logiciels ne poussent pas dans les arbres, en l’occurrence Winium est la création de Nick Abalov, actuellement développeur chez Badoo. Sur Twitter, il a gentiment répondu à notre question « Winium est-il mort ? » :

For Windows Desktop automation you might want to look into https://t.co/2agvjg2Kdj

— Nick Abalov (@NickAb) 20 décembre 2018

<script>

A suivre alors, WinAppDriver présentant cependant la contrainte de nécessiter un environnement Windows 10.

Verdict

Pour nous, c’est donc un no-go. Il est possible de mettre en place de petits projets d’automatisation en utilisant Winium, mais entamer un projet ambitieux avec cet outil serait imprudent.

Et vous, quels frameworks gratuits d’automatisation d’applications lourdes utilisez-vous ?