ZK Testing with Selenium IDE fr"

From Documentation
m (Created page with "{{Template:Smalltalk_Author| |author=Robert Wenzel, Engineer, Potix Corporation |date=July 30, 2013 |version=ZK 6.5 (or later) }} =Introduction= So you have your nice ZK appli...")
 
 
(26 intermediate revisions by the same user not shown)
Line 8: Line 8:
 
=Introduction=
 
=Introduction=
  
So you have your nice ZK application and are interested in, how to be sure it will work in different browsers (and keep working after changes, which always happen). Of course you have tested everything possible with your unit and integration tests ;-). You have maxed out the possibilities offered by [http://www.zkoss.org/product/zats ZATS] to ensure ZK components play well together in a simulated environment.
+
Vous avez donc votre chouette application ZK et vous aimeriez maintenant être de certain qu'elle va fonctionner correctement sur différents navigateurs (et continuera de fonctionner après de changements, ce qui survient souvent de nos jours...). Vous avez bien entendu testé tout ce qui était possible avec votre test unitaire et des tests d'intégration ;-). Vous avez essayé toutes les possibilités offertes par [http://www.zkoss.org/product/zats ZATS] pour avoir la certitude que vos composants ZK fonctionnent correctement ensemble dans un environnement simulé.
  
Finally you come to a point when it is necessary to ensure your application (or at least the key features) works and renders well in a real environment (with different browsers, real application server, real network traffic/latency etc.) - so eventually Selenium comes into play.
+
Vous en arrivez donc finalement au point de vouloir garantir le bon fonctionnement de votre application (ou au moins les éléments essentiels de celle-ci) dans un environnement réel (avec d'autres navigateurs, serveur réel d'applications, trafic et latence réseaux réels etc.) - alors, Selenium entre en jeux.
  
==About this article==
+
== À propos de cet article==
  
There are several approaches in creating Selenium Tests:
+
Différentes approches existent pour créer des tests avec Selenium:
  
The programmatic way - suitable for developers - is already covered in [[Small_Talks/2009/February/How_to_Test_ZK_Application_with_Selenium | this Small Talk]]. This way offers maximum control over the test execution to write reusable, flexible, maintainable and even "intelligent" test cases. Of course writing such test cases may require some time and resources.
+
Par programmation - méthode préférée des développeurs - est déjà développée dans [[Small_Talks/2009/February/How_to_Test_ZK_Application_with_Selenium | ce Small Talk]]. Cette méthode vous offre un contrôle maximum sur l'exécution du test et vous permet d'écrire des tests réutilisables, flexibles, facile à mettre à jour et même des cas "intelligents". Bien sure, pour écrire de tels tests, il faut du temps et des ressources.
  
Sometimes it is up to a less technical person to create these test cases. This is when one often hears about '''"record and replay"''' tools.
+
Il arrive parfois qu'une personne ayant un profil moins technique doive écrire ces tests. On entend alors souvent parler d'outils '''"record and replay"'''.
Selenium IDE is not only such a tool, it has record and replay features but also many more options to tweak and extend your test cases after recording. In few cases the recorded tests can be instantly re-playable. However, especially when it comes to AJAX there are several issues, that will prevent your test cases from executing out-of-the-box.
+
Selenium IDE possède cette caractéristique "record and replay" mais aussi beaucoup d'autres options adapter et étendre les tests après enregistrement. Dans de raraes cas, les tests enregistrés peuvent être rejoués de façon instantanée. Cependant, particulièrement avec AJAX, certains éléments vont permettre d'éviter l'exécution de vos tests hors carde.
  
Here are two examples (you can easily find more):
+
Voici deux exemples (et vous pouvez facilement en trouver d'autres):
* problems with [http://stackoverflow.com/questions/4203559/some-mouse-clicks-dont-register-in-selenium Missing clicks]
+
* problèmes avec [http://stackoverflow.com/questions/4203559/some-mouse-clicks-dont-register-in-selenium Missing clicks]
* someone recorded this [http://www.youtube.com/watch?v=k7U2mh65Xys video] explaining several issues
+
* quel'qu'un a enregistré ceci [http://www.youtube.com/watch?v=k7U2mh65Xys video] qui explique quelques problèmes
But not to worry, this article will show you how to deal with these!  
+
Mais pas de panique, cet article va vous expliquer comment gérer ces cas!  
  
The major conclusion is:
+
La conclusion majeure est:
See Selenium IDE more as a record/tweak/maintain and replay/debug tool to build your tests cases.
+
Voyez plutôt Selenium IDE comme un outil d'enregistrement/adaptation/maintenance et de ré-exécution/débogage lorsque vous construisez vos tests.
  
This article will show (using an example web application) how to:
+
Cet article va montrer (via une application web exemple) comment:
* enable useful initial test recording in ZK
+
* autoriser un enregistrement de test initial dans ZK
* tweak the tests so that they replay
+
* adapter les tests pour pouvoir les rejouer
* keep your tests maintainable and robust to changes in your pages
+
* garder vos tests maintenables et robustes face aux changements dans vos pages
* add missing actions that fail to record at all
+
* ajouter les actions manquantes
  
Many ideas in the Article are based on solutions found for general problems in several forums and previous project experiences. Some simply reflects my personal preference on how things may be handled. This article does not show the only way to write tests for a ZK application, just highlight problems and solutions, and discuss some considerations taken and why this approach was chosen.
+
Nombre d'idées dans cet article sont basées sur des solutions à des problèmes généraux depuis certains forums et aussi l'expérience des projets passés. Certaines sont simplement le reflet de mes préférences personnelles sur la manière de gérer les choses. Cet article ne montre pas une manière unique d'écrire des tests pour des applications ZK mais met juste en évidence des problèmes et des solutions, discute de certains considérations prises, et explique pourquoi cette approche a été choisie.
  
Happy reading and commenting !!
+
Bonne lecture et n'hésitez pas à commenter !!
  
 
==Environment==
 
==Environment==
 
* Firefox 22
 
* Firefox 22
* Selenium IDE 2.1.0 (Selenium IDE 2.0.0 is not working in FF >=22)
+
* Selenium IDE 2.1.0 (Selenium IDE 2.0.0 ne fonctionne pas avec FF >=22)
 
* JDK 6/7
 
* JDK 6/7
 
* ZK 6.5.3
 
* ZK 6.5.3
 
* Maven 3.0
 
* Maven 3.0
  
=Steps=
+
=Étapes=
==Initialize your environment for this example==
+
==Initialisez votre environnement pour cet exemple==
* Install Selenium IDE plugin in Firefox via Addons or [http://docs.seleniumhq.org/download/ download] [http://wiki.openqa.org/display/SIDE/Home documentation page]  
+
* Installez le plugin Selenium IDE dans Firefox via Addons ou [http://docs.seleniumhq.org/download/ download] [http://wiki.openqa.org/display/SIDE/Home documentation page]  
 
* Maven 3.0 [http://maven.apache.org/guides/getting-started/maven-in-five-minutes.html#Installation download & install]
 
* Maven 3.0 [http://maven.apache.org/guides/getting-started/maven-in-five-minutes.html#Installation download & install]
* [[#Download|download]] the example project, it contains the following folders:
+
* [[#Download|download]] le projet exemple, il contient les dossiers suivants:
** selenium_example/ (the example application, maven project)
+
** selenium_example/ (l'application exemple, le projet maven)
** extensions/ (the selenium extensions used in this article)
+
** extensions/ (les extension selenium utilisées dans cet article)
** test_suite/ (a Selenium Test Suite illustrating the contepts discussed here)
+
** test_suite/ (une suite de tests Selenium illustrant les concepts discutés ici)
* run on command line
+
* lancez depuis une ligne de commande
 
  cd selenium_example_app
 
  cd selenium_example_app
 
  mvn jetty:run
 
  mvn jetty:run
: or execute ''selenium_testing_app/launch_for_PROD.bat / .sh''
+
: ou exécutez ''selenium_testing_app/launch_for_PROD.bat / .sh''
* open test app http://localhost:8080/selenium_testing
+
* ouvrez l'application test http://localhost:8080/selenium_testing
  
==Initial Situation==
+
==Situation initiale==
# Launch Firefox
+
# Lancez Firefox
# Launch Selenium IDE CTRL+ALT+S
+
# Lancez Selenium IDE CTRL+ALT+S
# Set the base URL to "http://localhost:8080/"
+
# Mettez l'URL de base à "http://localhost:8080/"
# open example app http://localhost:8080/selenium_testing
+
# Ouvrez l'application exemple http://localhost:8080/selenium_testing
# Record login-test and try to replay it
+
# Enregistrez le login-test et essayez de le ré-exécuter
  
 
<table cellpadding="1" cellspacing="1" border="1">
 
<table cellpadding="1" cellspacing="1" border="1">
Line 92: Line 92:
 
</table>
 
</table>
  
Looks hard to read / maintain and doesn't even work :'(
+
Cela parait difficile à lire / maintenir et ne fonctionne même pas :'(
  
Looking at the recorded commands and comparing the IDs with the page source we notice the IDs are generated and changing with every page refresh. So no chance to record / replay this way.
+
En jetant un coup d’œil aux commandes enregistrées et en comparant les IDs à la page source, nous remarquons que ces derniers sont générés et changés à chaque rafraîchissement de la page. Aucune chance donc d'enregistrer / rejouer de cette manière.
  
A way to make the IDs predictable is shown in the next Section.
+
Une manière de rendre les IDs prévisibles est montrée dans la section suivante.
  
==Custom Id Generator==
+
==Générateur d'ID sur mesure==
  
As mentioned in other Small Talks about testing, one strategy is to use a custom IdGenerator implementation to create predictable, readable (with a business meaning) and easily selectable (by selenium) component IDs.
+
Comme mentionnée dans l'autre Small Talks sur les tests, une stratégie est d'utiliser une implémentation d'un générateur d'ID sur mesure pour créer des ID's de composants qui soient prévisibles, lisibles (avec un nom business) et facilement sélectionnable par selenium.
  
 
<source lang="java">
 
<source lang="java">
Line 127: Line 127:
 
</source>
 
</source>
  
The IDs will look like this:
+
Les IDs ressembleront à ceci:
* {id}_{tag}_{##} (for components with a given id)
+
* {id}_{tag}_{##} (pour les composants avec un id donné)
* {tag}_{##} (for components without a given id - e.g. listitems in a listbox)
+
* {tag}_{##} (pour les composants sans id donnée - p.e. listitems dans une listbox)
* {zkcomp}_{##} (for other cases, just to make them unique)
+
* {zkcomp}_{##} (pour les autres cas, histoire simplement de les rendre uniques)
  
e.g. a button in zul-file
+
p.e. un bouton dans un fichier zul  
 
  <button id="login" label="login" />
 
  <button id="login" label="login" />
will become something like this in HTML in browser (number can vary):
+
deviendra quelque chose comme ceci en HTML dans un navigateur (le nombre peut varier):
 
  <button id="login_button_12" class="z-button-os" type="button">login</button>
 
  <button id="login_button_12" class="z-button-os" type="button">login</button>
  
In order to separate production from test configuration we can include an additional config file at startup to enable the TestingIdGenerator only for testing.
+
Pour séparer la production de la configuration test, on peut inclure un ficher de config supplémentaire au démarrage pour autoriser le TestingIdGenerator uniquement dans les tests.
In ZK this is possible by setting the library property [[ZK_Configuration_Reference/zk.xml/The_Library_Properties/org.zkoss.zk.config.path|org.zkoss.zk.config.path]] (also refer to our [[ZK_Developer%27s_Reference/Testing/Testing_Tips#Different_Configuration_for_Different_Environment|Testing Tips]])
+
Avec Zk, ceci est possible en configurant la propriété de librairies
 +
[[ZK_Configuration_Reference/zk.xml/The_Library_Properties/org.zkoss.zk.config.path|org.zkoss.zk.config.path]] (voir aussi notre [[ZK_Developer%27s_Reference/Testing/Testing_Tips#Different_Configuration_for_Different_Environment|Testing Tips]])
  
:e.g. via VM argument -Dorg.zkoss.zk.config.path=/WEB-INF/zk-test.xml
+
:p.e. via l'arguement VM -Dorg.zkoss.zk.config.path=/WEB-INF/zk-test.xml
  
 
Windows:
 
Windows:
Line 150: Line 151:
 
  mvn jetty:run
 
  mvn jetty:run
  
or execute ''selenium_testing_app/launch_for_TEST.bat / .sh''
+
ou exécutez ''selenium_testing_app/launch_for_TEST.bat / .sh''
  
  
Now we get nicer and deterministic IDs when recording a test case.
+
On a maintenant des ID's déterminés lors de l'enregistrement d'un test.
  
After recording login-test again and we get this:
+
Après l'enregistrement, on relance le test et on obtient ceci:
  
 
<table cellpadding="1" cellspacing="1" border="1">
 
<table cellpadding="1" cellspacing="1" border="1">
Line 181: Line 182:
 
</table>
 
</table>
  
This already looks nice, and self explaining - but unfortunately still fails to replay
+
Ça parait déjà mieux et ça s'explique de sois-même, mais malheureusement cela no fonctionne toujours pas si on le relance.
  
Why this happens and how to fix it? See next section ...
+
Pourquoi cela se passe-t-il et comment le régler ? Voyons la section suivante ...
  
==ZK specific details==
+
==Détails spécifiques à ZK==
  
Due to the nature of rich web applications, JavaScript is generally heavily used, but Selenium IDE does not record all events by default. In our case here the "blur" events of the input fields have not been recorded, but ZK relies on them to determine updated fields. So, we need to manually add selenium commands "fireEvent target blur".Unfortunately Selenium does not offer a nice equivalent like "focus" action.
+
Vu la nature des applications web riches, JavaScript est souvent très utilisé mais Selenium IDE n'enregistre pas par défaut tous les événements. Dans notre cas, les événements "blur" des champs d'entrée n'ont pas été enregistrés alors que ZK se base sur eux pour déterminer les champs mis à jour. On doit donc ajouter manuellement la commande selenium "fireEvent target blur". Selenium n'offre malheureusement pas d'équivalente à "focus".
  
 
<table cellpadding="1" cellspacing="1" border="1">
 
<table cellpadding="1" cellspacing="1" border="1">
Line 238: Line 239:
 
</table>
 
</table>
  
Replaying the script now works :D, and we see the overview page (also added a few verifications for that).
+
Si on relance le script, maintenant cela fonctionne :D et on voit la page overview (on a aussi ajouté quelques vérifications pour cela).
  
==Selenium Extensions==
+
==Extensions Selenium==
  
If you find the syntax of "fireEvent" hard to remember or think it is too inconvenient to add an additional line just to update an input field there's help using a '''Selenium Core extension'''. The extension - file usually called '''user-extensions.js''' can be used in both Selenium IDE and Selenium RC when running the tests outside of Selenium IDE (please refer to [http://docs.seleniumhq.org/docs/08_user_extensions.jsp selenium documentation]).
+
Si vous avez le sentiment qu'il est difficile de se souvenir de la syntaxe de "fireEvent" ou si vous pensez qu'il est trop compliqué d'ajouter une ligne additionnelle juste pour mettre à jour un champ d'entrée, utiliser une '''Selenium Core extension''' peut vous aider. Le fichier d'extension, généralement appelé '''user-extensions.js''' peut être utilisé par Selenium IDE et par Selenium RC lorsque les tests se font en dehors de Selenium IDE (merci de vous référer à [http://docs.seleniumhq.org/docs/08_user_extensions.jsp selenium documentation]).
  
This snippet shows 2 simple custom actions:
+
L'extrait suivant montrent deux actions simples:
 
;blur
 
;blur
: convenient action to avoid "fireEvent locator blur"
+
: pratique pour éviter "fireEvent locator blur"
 
;typeAndBlur  
 
;typeAndBlur  
: combines the type with a blur event, to make ZK aware of the input change automatically
+
: combine le type avec un événement blur, pour rendre ZK attentif aux changement de la valeur entrée
  
 
<source lang="javascript">
 
<source lang="javascript">
Line 269: Line 270:
 
</source>
 
</source>
  
To enable it just add the file (''extensions/user-extension.js'' in the example zip) to the Selenium IDE configuration.
+
Pour l'activer, ajoutez le fichier (''extensions/user-extension.js'' dans le zip de l'exemple) à la configuration de Selenium IDE.
(the file contains another extension ... more about when discussing [[#Custom_Locator_and_LocatorBuilder|custom locators]])
+
(le fichier contient une autre extension ... voir [[#Custom_Locator_and_LocatorBuilder|custom locators]])
 
: (Selenium IDE Options > Options... > [General -Tab] > Selenium Core extensions)
 
: (Selenium IDE Options > Options... > [General -Tab] > Selenium Core extensions)
Then restart Selenium IDE - close the window, and reopen it - e.g. by [CTRL+ALT+S]
+
Redémarrez ensuite Selenium IDE - fermez la fenêtre et rouvrez-la - p.e. avec [CTRL+ALT+S]
  
Adapted test using the '''blur''' action:  
+
Test adapté qui utilise l'action '''blur''':  
 
<table cellpadding="1" cellspacing="1" border="1">
 
<table cellpadding="1" cellspacing="1" border="1">
 
<tr><th rowspan="1" colspan="3">login test</th></tr>
 
<tr><th rowspan="1" colspan="3">login test</th></tr>
Line 309: Line 310:
 
</table>
 
</table>
  
Same test using the '''typeAndBlur''':
+
Même test en utilisant '''typeAndBlur''':
 
<table cellpadding="1" cellspacing="1" border="1">
 
<table cellpadding="1" cellspacing="1" border="1">
 
<tr><th rowspan="1" colspan="3">login test</th></tr>
 
<tr><th rowspan="1" colspan="3">login test</th></tr>
Line 334: Line 335:
 
</table>
 
</table>
  
It is not required to use either of these extensions, but saving 1 line for each input is my personal preference.
+
Il n'est pas nécessaire d'utiliser l'une de ces extensions mais ma préférence personnelle va à l'économie d'une ligne pour chaque entrée.
  
Another thing to keep in mind is robustness and maintenance effort of test-cases when changes in the UI happen ... the generated numbers at the end of each ID are still an obstacle in this way as they are prone to change every time a component is added or removed above that component.
+
Une autre chose à garder à l'esprit est le manque de robustesse et l'effort de maintenance à fournir pour les tests quand des changements apparaissent dans l'UI ... les numéros générés à la fin de chaque ID restent un obstacle parce qu'ils sont sujets à changer chaque fois qu'un composant est ajouté ou retiré au dessus de ce composant.
  
Wouldn't it be nice to avoid having to adapt test cases that often? Read on...
+
Ne serait-il pas sympa de n'avoir à adapter aussi souvent les tests ? Voyons la suite...
  
==Improve robustness and maintainability==
+
==Améliorer la robustesse et la maintenance==
  
===Locators in Selenium===
+
===Locators dans Selenium===
  
To remove the hard coded running numbers of the component IDs from our test cases Selenium offers e.g. XPath or CSS locators.
+
Pour enlever les nombres hard codés des ID composants de notre test, Selenium offre p.e. XPath ou CSS locators.
  
Using XPath is it possible to select a node by its ID-prefix:
+
En utilisant XPath, est-il possible de sélectionner un nœud à l'aide de son préfixe ID ?:
  
 
  //input[starts-with(@id, 'username_')]
 
  //input[starts-with(@id, 'username_')]
  
This will work as long as the prefix "username_" is unique on the page, otherwise will perform the action on the first element found. So it is a good idea for this scenario to give widgets suitable IDs (plus: it will also improve the readability of source code).
+
Cela va fonctionner tant que le préfixe "username_" est unique sur la page, sans quoi l'action sera exécutée sur le premier élément trouvé. C'est donc une bonne idée, dans ce scénario, de donner aux widgets des ID appropriés (plus: cela va aussi augmenter la lisibilité du code source).
  
In more complex cases one can select nested components to distinguish components with the same ID (e.g. in different ID spaces).
+
Dans des cas plus complexes, on peut sélectionner des composants imbriqués pour distinguer les composants avec le même ID (p.e. dans des espaces ID différents).
e.g. if the "street" component appears several times on the page use this:
+
p.e. si le composant "street" apparaît plusieurs fois sur la page, utilisez ceci:
  
 
  //div[starts-with(@id, 'deliveryAddress_')]//input[starts-with(@id, 'street_')]
 
  //div[starts-with(@id, 'deliveryAddress_')]//input[starts-with(@id, 'street_')]
 
  //div[starts-with(@id, 'billingAddress_')]//input[starts-with(@id, 'street_')]
 
  //div[starts-with(@id, 'billingAddress_')]//input[starts-with(@id, 'street_')]
  
Another example to locate the "delete" button in the currently selected row of a listbox using ID prefixes, CSS-class and text comparison.
+
Un autre exemple pour localiser le bouton "delete" dans la ligne sélectionnée d'une listbox qui utilise des préfixes ID, CSS-class et comparaison de texte.
  
 
  //div[starts-with(@id, 'overviewList_')]//tr[contains(@class, 'z-listitem-seld')]//button[text() = 'delete']
 
  //div[starts-with(@id, 'overviewList_')]//tr[contains(@class, 'z-listitem-seld')]//button[text() = 'delete']
  
NOTE: Make sure not too use the "//" operator too extensively in XPath, as it might perform badly (for me this never was a matter so far, as there are ''much worse'' performance bottle necks, affecting your test execution speed - [[#Waiting for AJAX|discussed later]]).
+
NOTE: Veillez à ne pas utiliser l'opérateur "//" de façon trop extensive dans XPath, cela pourrait ne pas fonctionner (cela n'a jamais été un problème pour moi jusqu’à présent vu qu'il y a des goulots d’étranglement  ''bien pire'' en terme de performance qui affectent la vitesse d'exécution de votre test - [[#Waiting for AJAX|voir plus tard]]).
  
This [https://www.simple-talk.com/dotnet/.net-framework/xpath,-css,-dom-and-selenium-the-rosetta-stone/ sheet provided by Michael Sorens] is an excellent reference with many helpful examples on how to select page elements in various situations.
+
Ce lien [https://www.simple-talk.com/dotnet/.net-framework/xpath,-css,-dom-and-selenium-the-rosetta-stone/ sheet provided by Michael Sorens] est un excellente référence avec plein d'exemples utiles sur comment sélectionner des éléments de page dans diverses situations.
  
So now we can change the test case to use this kind of XPath locator, searching only by ID prefix:
+
On peut donc maintenant changer le test pour utiliser cette sorte de XPath locator, en cherchant uniquement avec le préfixe ID:
 
<table cellpadding="1" cellspacing="1" border="1">
 
<table cellpadding="1" cellspacing="1" border="1">
 
<tr><th rowspan="1" colspan="3">login test</th></tr>
 
<tr><th rowspan="1" colspan="3">login test</th></tr>
Line 391: Line 392:
 
</table>
 
</table>
  
Done that, the order of the components may change without breaking the test, as long as the actual IDs are not changed. Of course this requires some manual work, but the effort invested once will pay off quickly as your project evolves.
+
Ceci fait, l'ordre des composants peut changer sans casser le test, tant que les IDs ne sont pas changés. Ceci requirt bien entendu un peu de travail manuel mais l'effort investi une fois payera rapidement au cours de l'évolution des votre projet.
  
===Custom Locator and LocatorBuilder===
+
===Custom Locator et LocatorBuilder===
  
If you don't want to change the locator to XPath manually every time after you record a test case, Selenium IDE has more to offer. Even improving the readability.
+
Si vous ne voulez pas changer le locator vers XPath de façon manuelle à chaque fois que vous enregistre un test, Selenium IDE vous offre une solution qui va encore augmenter la lisibilité.
  
 
====Custom Locator====
 
====Custom Locator====
: In the ''user-extensions.js'' from above you'll find a custom locator that will hide some of the XPath complexity for simple locate scenarios. It is based on the format of IDs generated by TestingIdGenerator (from above).
+
: Dans le ''user-extensions.js'' d'ici plus haut, vous allez trouver un custom locator qui va cacher une partie de la complexité de XPath pour des scénarios simples. C'est basé sur le format des IDs générés par TestingIdGenerator (voir plus haut).
  
 
<source language="javascript">
 
<source language="javascript">
Line 421: Line 422:
 
</source>
 
</source>
  
This locator will work in 2 forms
+
Ce locator va travailler selon 2 formes
  
 
  1. zktest=#login_
 
  1. zktest=#login_
 
  2. zktest=input#username_
 
  2. zktest=input#username_
  
Internally generated/queried XPaths will be
+
XPaths généré/questionné en interne sera
  
 
  1. //*[starts-with(@id, 'login_')]
 
  1. //*[starts-with(@id, 'login_')]
 
  2. //input[starts-with(@id, 'username_')]
 
  2. //input[starts-with(@id, 'username_')]
  
# will look for any element with an ID starting with "login_"
+
# cherchera les éléments dont l'ID commence par "login_"
# will also test the elements html tag, and find the <input> element with the ID-prefix "username_..."
+
# testera aussi les éléments avec tag html, et trouvera l'élément <input> avec le préfixe ID "username_..."
  
In addition, it is possible to append any further XPath to narrow down the results.
+
De plus, il est possible de joindre n'importe quel XPath supplémentaire pour affiner les résultats.
  
e.g. (to locate the second option in a <select> element)
+
p.e. (pour localiser la deuxième option d'un élement <select>)
 
  zktest=select#priority_ /option[2]
 
  zktest=select#priority_ /option[2]
will become in XPath
+
deviendra dans XPath
 
  //select[starts-with(@id, 'priority_')]/option[2]
 
  //select[starts-with(@id, 'priority_')]/option[2]
  
Even if this locator is very basic (could be extended if one needs), It already improves readability of many use-cases.
+
Même si ce locator est très basique (peut être étendu si besoin), il augmente déjà la lisibilité de nombreux use-cases.
  
 
====Custom LocatorBuilder====
 
====Custom LocatorBuilder====
  
In order to make this really handy, Selenium IDE offers extensions too ('''don't mix this up with Selenium Core Extension''')  
+
Pour rendre ceci très maniable, Selenium IDE offre aussi des extensions ('''ne pas mélanger avec Selenium Core Extension''')  
  
Add IDE extension file ''extensions/zktest-Selenium-IDE-extension.js'' from example.zip to selenium config
+
Ajoutez le ficher extension IDE ''extensions/zktest-Selenium-IDE-extension.js'' depuis example.zip à la config selenium
 
: (Selenium IDE Options > Options... > [General -Tab] > Selenium IDE extensions)
 
: (Selenium IDE Options > Options... > [General -Tab] > Selenium IDE extensions)
  
and move it up in the Locator Builder priority list.
+
et déplacez le vers le haut dans la liste de priorités du Locator Builder.
 
: (Selenium IDE Options > Options... > [Locator Builders - Tab])
 
: (Selenium IDE Options > Options... > [Locator Builders - Tab])
  
It contains the following LocatorBuilder which will generate the locator in form "zktest=elementTag#IdPrefix" mentioned above using only the first part of the ID (the ID prefix - its business name). If an ID does not contain 3 tokens separated by "_" it will use the full ID instead of including the sequential number, this will indicate that the element has no ID specified and another location approach may be better, or just give it an ID in the zul file.
+
Il contient le LocatorBuilder suivant qui va générer le locator dans la forme "zktest=elementTag#IdPrefix" mentionnée ci-dessus en utilisant uniquement la première partie de l'ID (le préfixe ID - son nom business). Si un ID ne contient pas 3 tokens séparés par "_", il utilisera l'ID complet à la plac en incluant le numéro séquentiel. Ceci indiquera que l'élément n'a pas d'ID spécifié et une autre approche de localisation peut être meilleure, ou donnez lui juste un ID dans le fichier zul.
  
 
<source language="javascript">  
 
<source language="javascript">  
Line 469: Line 470:
 
</source>
 
</source>
  
Restart Selenium IDE and record the test case again... and you'll get this (after changing the ''type'' to ''typeAndBlur'' events).
+
Redémarrez Selenium IDE et enregistrez le test à nouveau... vous aurez ceci (après avoir changé les événements de ''type'' à ''typeAndBlur'').
  
 
<table cellpadding="1" cellspacing="1" border="1">
 
<table cellpadding="1" cellspacing="1" border="1">
Line 495: Line 496:
 
</table>
 
</table>
  
=More Tests=
+
=Plus de Tests=
  
Let's record a test case for editing and saving an existing "feedback item" in the list, verify the results, and a logout test.
+
Faisons un test pour l'édition et la sauvegarde d'un "feedback item" existant dans la liste, vérifions les résultats et faisons un test de lougout.
These tests introduce new challenges testing an ajax applications with Selenium - and their solution or workarounds.
+
Ces tests introduisent des nouveaux challenges pour tester une application ajax avec Selenium - et leurs solutions ou contournements.
  
Now that we are actually changing data I adapted the login test, to use a random user each time. So that the Mock implementation will serve fresh data for each test run, without having to restart the server, or having to cleanup (there are other strategies possible to do the same - that's just what I use here).
+
Maintenant que nous changeons les données, j'ai adapté le test de login pour utiliser un utilisateur aléatoire à chaque fois. Dès lors, l'implémentation Mock fournira des données fraîches à chaque essai, sans avoir à redémarrer le serveur ni à faire de nettoyage (il existe d'autres stratégies pour faire la même chose, c'est simplement celle que j'utilise ici).
  
 
<table cellpadding="1" cellspacing="1" border="1">
 
<table cellpadding="1" cellspacing="1" border="1">
Line 541: Line 542:
 
</table>
 
</table>
  
==Edit and Save Test==
+
==Test Edit et Save ==
  
After recording and applying the '''typeAndBlur''' actions we get the following.
+
Après avoir enregistré et appliqué les actions '''typeAndBlur''', on obtient ceci:
  
 
<table cellpadding="1" cellspacing="1" border="1">
 
<table cellpadding="1" cellspacing="1" border="1">
Line 619: Line 620:
 
</table>
 
</table>
  
We still see some hard-coded numbers. I'll just delete the "click zktest=#listitem_62" as the "select" will command will just do fine (Selenium IDE will sometimes record more than you need, and sometimes not enough - by default).
+
Nous voyons toujours quelques valeurs hard-codées. Je vais juste supprimer "click zktest=#listitem_62" vu que la commande "select" fera l'affaire (Selenium IDE va parfois enregistrer plus que nécessaire, et parfois pas assez - par défaut).
  
Interesting for now is the locator "zktest=#button_27" - one of the the "edit" button. If we try to give it an ID in the zul code it will fail to render (duplicate ID), because the button is rendered multiple times - once for each <listitem>. (one workaround could be to surround it by an <idspace> component or include the buttons from a different file). But it is not necessary - using the right locator XPath.
+
Ce qui est intéressant maintenant est le locator "zktest=#button_27" - un des boutons "edit". Si nous essayons de lui donner un ID dans le code zul, cela ne donnera aucun résultat (ID dupliqué) pcq le bouton est déclaré plusieurs fois - à chaque <listitem>. (un contournement possible serait de l'encadrer avec un composant <idspace> ou d'inclure les boutons depuis un fichier différent). Mais cela n'est pas nécessaire - en utilisant le bon locator XPath.
  
===Locating an Element inside a Listbox===
+
===Localiser un élement dans une Listbox===
  
If you just want to locate the first edit button in the list you can use (it will find the button by its text and not by its ID):
+
Si vous voulez localiser le premier bouton edit dans une liste, vous pouvez utiliser(il trouvera le bouton par son texte et non son ID):
  
 
pure XPath:
 
pure XPath:
 
  //*[starts-with(@id, 'overviewList_')]//button[text() = 'edit']
 
  //*[starts-with(@id, 'overviewList_')]//button[text() = 'edit']
  
or combine with the "zktest" selector from the selenium extension above
+
ou combiner avec le selecteur "zktest" de l'extension selenium vue précédemment
 
  zktest=#overviewList_ //button[text() = 'edit']
 
  zktest=#overviewList_ //button[text() = 'edit']
  
more interesting is it to locate by index, e.g. exactly the second edit button
+
Il est plus intéressant de localiser par l'index, p.e. exactement le deuxième bouton edit
 
  xpath=(//*[starts-with(@id, 'overviewList_')]//button[text() = 'edit'])[2]
 
  xpath=(//*[starts-with(@id, 'overviewList_')]//button[text() = 'edit'])[2]
  
or even more fun to select the "edit" button inside a <listitem> containing a specific text in a cell
+
ou plus marrant de sélectionner le bouton "edit" dans un <listitem> contenant un texte spécifique dans une cellule
 
  xpath=//*[starts-with(@id, 'overviewList_')]//*[starts-with(@id, 'listitem_')][.//*[starts-with(@id, 'listcell_')][text() = 'Subject 2']]//button[text() = 'edit']
 
  xpath=//*[starts-with(@id, 'overviewList_')]//*[starts-with(@id, 'listitem_')][.//*[starts-with(@id, 'listcell_')][text() = 'Subject 2']]//button[text() = 'edit']
This can get infinitely complex, and reduce the readability of your testcase. That's why it is also a good idea to write '''comments''' (see screenshot below) in your test case (yes, test cases can have comments too).
+
Cela peut devenir très complexe et diminuer la lisibilité de votre test. C'est donc toujours une bonne idée d'écrire des '''comments''' (voir screenshot en dessous) dans vos test (oui, ils peuvent aussi avoir des commentaires).
  
Even though complex this last locator is still quite stable to changes in the UI. Especially when the <listbox> content changes, you can still use it to find the same <listitem> (and its contained button) several times in a test case.
+
Bien que compliqué, ce dernier locator reste stable et fait face aux changements de l'UI. Si le contenu de <listbox> changes, vous pouvez toujours l'utiliser pour trouver le <listitem> (et le bouton qu'il contient) plusieurs fois au cours d'un test.
  
===Using Variables===
+
===Utiliser des variables===
  
Repeating those complex locators in the test scripts will give you a headache maintaining them, when e.g. the label of the button changes from "edit" to "update". It is better to "store" locator strings or parts of them in variables, which can be reused throughout the test.
+
Maintenir ces locators dans vos scripts de tests va vous donner la migraine, p.e. si le label d'un bouton change de "edit" à "update". Il est plus simple de "stocker" les chaines de caractères, ou une partie de celles-ci, dans des variables qui peuvent être réutilisées tout au long du test.
  
 
[[File:variables_and_comments.png|variables and comments example]]
 
[[File:variables_and_comments.png|variables and comments example]]
  
These are just a few general ideas, about what can be done to reduce the work in maintaining test cases, increasing stability or make them more readable - at reasonable investment when setting them up (recording and adjusting) initially.
+
Ce ne sont que quelques idées générales sur comment réduire le travail de maintenance des tests, en augmentant leur stabilité ou en les rendant plus lisibles - moyennant un investissement raisonnable lors de la mise sur pied initiale.
  
==Waiting for AJAX==
+
==Attendre AJAX==
  
However rerunning the test suite at full speed (which is what we should always aim for) will or may sometimes - fail, because we don't wait long enough for ajax responses to render... there is no full page reload so Selenium IDE does not wait automatically.
+
Bien que la ré-exécution du test à pleine vitesse (ce qui est toujours l'objectif) va, ou pourrait parfois, échouer (pcq on attend pas assez longtemps les réponses d'ajax)... il n'y a pas de rechargement complet de page, donc Selenium IDE n'attend pas de manière automatique.
  
An idea might be to '''reduce the test speed'''... which would affect every step in all test cases, so we try to avoid that. Also in our feedback example there is a longer operation when clicking the "submit" button (2 seconds). Delaying every Step by 2 seconds would be devastating for our overall replay duration.
+
Une idée serait de '''réduire la vitesse du test'''... ce qui affecterait chaque étape dans tous les tests, nous essayons donc d'éviter cela. Aussi, dans notre exemple de feedback, l'opération du click sur le bouton "submit" est longue (2 secondes). Retarder chaque étape de 2 secondes provoquerait des dégâts considérables sur la durée de notre replay global.
  
Another possibility is using the '''pause''' command and specify the number of milliseconds to wait. Also here the execution speed might vary so we are either waiting too short, so that errors still occur, or we wait too long ("just to be sure") and waste time - both not desirable.
+
Un autre possibilité est d'utiliser la commande '''pause''' et spécifier le nombre de millisecondes à attendre. Ici aussi la vitesse d'exécution peut varier et nous serons donc trop court, ce qui va engendrer des erreurs, ou trop longs ("juste pour être certain") et gaspiller du temps, ces deux effets étant indésirables.
  
Luckily Selenium comes with a variety of '''waitFor...''' actions, which stops the execution until a condition is met. A useful subsection is:
+
Heureusement, Selenium fournit une variété d'actions du type '''waitFor...''' qui interrompent l'exécution tant qu'une condition n'est pas rencontrée. Une section très utile:
* waitForTextPresent - wait until a text is present anywhere on the page
+
* waitForTextPresent - attend q'un texte soit présent sur le page
* waitForText - wait until an element matching a locator has the text
+
* waitForText - attend qu'un élément reçoive un texte
* waitForElementPresent - wait until an element matched by a locator becomes rendered
+
* waitForElementPresent - attend qu'une élément soit présenté
* waitForVisible - wait until an element matched by a locator becomes visible
+
* waitForVisible - attend qu'un élément devienne visible
  
The previously recorded "edit save test" would likely fail in line 3 just after the click on the edit button.
+
Le test "edit et save" précédent devrait échouer à la ligne 3 juste après le click sur le bouton "edit".
Between Step 2 and 3 ZK is updating parts of the page using AJAX, causing a short delay (but long enough for our test to fail - when running at full speed).
+
Entre l'étape 2 et l'étape 3, ZK met à jour la page avec des éléments d'AJAX, ce qui provoque un délais court (mais long assez que pour faire échouer le test - lorsqu'il est à pleine vitesse).
  
 
<table cellpadding="1" cellspacing="1" border="1">
 
<table cellpadding="1" cellspacing="1" border="1">
Line 716: Line 717:
 
</table>
 
</table>
  
So we have to wait e.g. until the new Headline "New/Edit Feedback" is present that the AJAX update has finished.
+
On doit donc attendre p.e. que le nouveau Headline "New/Edit Feedback" soit présent, ce qui indique que la mise à jour AJAX est terminée.
And wait after submitting the feedback article accordingly - replace the "assertTextPresent" with "waitForTextPresent". Also the last assert... can be replaced.
+
Et attendre en conséquence après l'envoi de l'article de feedback - remplacer "assertTextPresent" par "waitForTextPresent".
  
 
<table cellpadding="1" cellspacing="1" border="1">
 
<table cellpadding="1" cellspacing="1" border="1">
Line 793: Line 794:
 
</table>
 
</table>
  
Now this test can run as fast as possible adapting automatically, to any speedup, or slowdown of the test server.
+
Ce test peut maintenant fonctionner aussi vite que possible, s'adapter automatiquement à une quelconque augmentation ou diminution de vitesse du serveur de test.
  
It is also possible to '''implicitly wait''' in general for AJAX to finish at a technical level [[http://agilesoftwaretesting.com/selenium-wait-for-ajax-the-right-way/ covered here]]. Personally I think it is tempting at first. After a second thought, I prefer the idea to '''explicitly wait''' (via waitForXXX) for your expected results - only when there is need to wait.
+
Il est aussi possible '''d'attendre de façon implicite''' qu'AJAX ait fini à un niveau technique [[http://agilesoftwaretesting.com/selenium-wait-for-ajax-the-right-way/ traité ici]]. Je pense personnellement que c'est tentant au premier abord. Mais après réflexion, je préfère l'idée '''d'une attente explicite''' (via waitForXXX) - seulement quand il faut attendre.
  
Like that you find out directly that something on your page has affected the performance (= user experience) - in case the test suddenly fails after a change affecting the responsiveness of your application.
+
Vous pourez aussi trouver par vous-même quelque-chose sur votre page qui affecte la performance (= expérience utilisateur) - si un test échoue soudainement après un changement qui affecte la capacité de réponse de votre application.
  
Additionally, there might be another delay after the AJAX response has finished, until the expected component is finally rendered on the page or visible (e.g. due to an animation). So this approach may still fail - just bear that in mind.
+
De plus, il pourrait y avoir un autre délais entre le moment ou AJAX a terminé et le moment où le composant affecté par cette réponse est réellement visible sur la page(p.e. à cause d'une animation). Cette approche pourrait donc toujours échouer - à garder en mémoire.
  
==Logout Test==
+
==Test Logout ==
  
This test should be very simple, but still there is a catch. When trying to record this test, we notice that Seleniume-IDE won't record clicking on the logout button. Selenium IDE is not very predictable about which events are recorded and which are ignored. In this case the it is a <toolbarbutton> which is rendered as a &lt;div&gt; so maybe that's why.  
+
Ce test devrait être très simple, malgré tout il y a une attrape. En essayant d'enregistrer ce test, nous remarquons que Seleniume-IDE n'enregistre pas le click sur le boutone logout. Il est difficile de prévoir quels événements Selenium IDE va enregistrer, et quels sont ceux qu'il va ingorer. Dans ce cas-ci, c'est un <toolbarbutton> qui est présenté comme &lt;div&gt; c'est pet-être la raison.  
  
(There is a [[http://stackoverflow.com/questions/4203559/some-mouse-clicks-dont-register-in-selenium stackoverflow question about this]] and also an [[http://wiki.openqa.org/display/SIDE/Contributed+Extensions+and+Formats#ContributedExtensionsandFormats-Recordingeveryclicksonpage IDE extension]] available to enable recording of ALL clicks in a test. I tried this one - and didn't like the big number of clicks recorded. In the end I leave it up to you to uncomment this in the [[#Custom LocatorBuilder|IDE extension for Locator Builder]] provided above and judge yourself - maybe you want to test exactly this.)
+
(Voir [[http://stackoverflow.com/questions/4203559/some-mouse-clicks-dont-register-in-selenium ici]] et aussi [[http://wiki.openqa.org/display/SIDE/Contributed+Extensions+and+Formats#ContributedExtensionsandFormats-Recordingeveryclicksonpage une extension IDE]] disponible pour permettre l'enregistrement de tous les clicks lors d'un test. J'ai essayé celui-ci - et n'ai pas aimé le grand nombre de clicks enregistrés. J'ai finalement laissé ceci [[#Custom LocatorBuilder| extension IDE pour Locator Builder]] founi ci-dessus et jugez vous-même - vous désirez peut-être tester exactement cela.)
  
As we know its name (or we can easily find out with tools like firebug) we just add another click event manually and change the locator to "zktest=div#logout_"
+
Vu que nous connaissons son nom (ou on peut facilement le trouver avec un outil comme firebug), on ajoute simplement un autre événement click manuellement et nous changeons le locator à "zktest=div#logout_"
  
 
<table cellpadding="1" cellspacing="1" border="1">
 
<table cellpadding="1" cellspacing="1" border="1">
Line 828: Line 829:
 
</table>
 
</table>
  
=Other Components Examples=
+
=Exemples avec d'autres composants=
Other components require some extra attention too when recording events on them (sometimes you might wonder why the results are varying).
+
D'autres composants requièrent aussi une attention particulière lorsqu'on enregistre les événements dessus (vous pourriez vous demandez parfois pourquoi les résultats changent).
So here are just a few examples.
+
Voici donc quelques exemples.
==Button (mold="trendy")==
+
==Bouton (mold="trendy")==
The "new/edit page" of the example application contains 2 submit buttons - rendered using different molds: "default" and "trendy". Both have the same effect (save the feedback item). When you try to record the click event of the second one - labelled "submitTrendy" - Selenium IDE just ignores the click.  
+
La page "page new/edit" de l'exemple contient 2 boutons d'envoi - leurs rendus utilisent des moules différents: "default" et "trendy". Tous les deux ont le même effet  (sauvegarder l'élément de feedback). Lorsque vous essayer d'enregistrer l'événement click du deuxième - labellisé "submitTrendy" - Selenium IDE ingore complètement le click.  
  
Funny thing is, when you first focus another window on your desktop and then click the button directly - without prior focusing the browser window - it gets recorded. But our custom locator builder does not match here. So we get some other generic locator strings. (The "trendy" button is rendered using a layout table and images for the round corners.)
+
Et la chose marrante est lorsque vous mettez d'abord le focus dans une autre fenêtre et cliquez ensuite sur le bouton directement - sans remettre d'abord le focus dans la fenêtre-, il est enregistré. Mais notre locator builder ne correspond pas ici. On obtient donc d'autres chaines de caractère de locator génériques. (Le bouton "trendy" est présenté avec l'aide d'un tableau de layout et des images pour les coins arrondis.)
  
 
[[File:button-trendy-record.png|button trendy recorded locator]]
 
[[File:button-trendy-record.png|button trendy recorded locator]]
  
The second choice contains the component ID, we can use to build our own locator string.
+
Le second choix contient l'ID du composant, nous pouvons l'utiliser pour construire notre propre chaîne de caractères de locator.
 
  zktest=#submitTrendy_
 
  zktest=#submitTrendy_
This would usually be sufficient but not in this example. When we test it, clicking on the "Find" button in Selenium IDE we see it is selecting the whole cell around it. ZK has generated some html elements of the layout grid around our button sharing the same ID prefix.
+
Ceci serait généralement suffisant, mais pas dans cet exemple. Au cours du test, lorsqu'on clique sur le bouton "Find" dans Selenium IDE, on le voit sélectionner la cellule entière autour de celui-ci. ZK a généré quelques éléments html de la grille de layout autour de notre bouton et partageant le même préfixe ID.
Inspecting the element in the browser we can see the element we really want to click and that it is an <span> element with a style class "z-button"
+
En inspectant l'élément dans le navigateur, on voit que l'élément sur lequel on souhaite cliquer réellement est un élément <span> avec une classe de style "z-button"
 
<source><span id="submitTrendy_button_129" class="z-button"></source>
 
<source><span id="submitTrendy_button_129" class="z-button"></source>
  
So a more specific locator would look like this
+
Un locator plus spécifique ressemblerait à ceci
 
  zktest=span#submitTrendy_
 
  zktest=span#submitTrendy_
or that
+
ou à cela
 
  zktest=#submitTrendy_ [@class = "z-button"]
 
  zktest=#submitTrendy_ [@class = "z-button"]
  
equivalent XPaths are:
+
Les XPaths équivalents sont:
 
  //span[starts-with(@id, 'submitTrendy_')]
 
  //span[starts-with(@id, 'submitTrendy_')]
 
  //*[starts-with(@id, 'submitTrendy_')][@class="z-button"]
 
  //*[starts-with(@id, 'submitTrendy_')][@class="z-button"]
  
 
==Menu==
 
==Menu==
Recording events on a <menubar> (even with submenus) is generally very simple. Unless you click the "wrong" areas while recording highlighted in red see image below. If you just click the menu(item)-texts (green) Selenium IDE will create nice locators, otherwise it will fallback to some other strategy, using CSS or generated XPath which might surprise you (this is no bug in Selenium IDE, it is just a different html element receiving the click event).
+
Enregistrer les événements sur une  <barre de menu> (même avec des sous-menus) est généralement très simple. À moins que vous ne cliquiez la "mauvaise" zone lors de l'enregistrement(voir en rouge sur l'image en dessous). Si vous cliquez juste sur les menu(item)-textes (en vert), Selenium IDE va créer des bons locators, sans quoi il faut passer à d'autres stratégies utilisant CSS ou des XPath générés qui pourraient vous surprendre (ce n'est pas un bug de Selenium IDE, c'est juste un élément html différent qui reçoit l'événement click).
  
 
<table cellpadding="1" cellspacing="1" border="1">
 
<table cellpadding="1" cellspacing="1" border="1">
Line 872: Line 873:
 
[[File:menu-areas-to-avoid.png|avoid red areas, and click on the green areas]]
 
[[File:menu-areas-to-avoid.png|avoid red areas, and click on the green areas]]
  
What you get avoiding the '''red''' areas.
+
Ce que vous obtiendrez en évitant les zones "rouges".
  
 
<table cellpadding="1" cellspacing="1" border="1">
 
<table cellpadding="1" cellspacing="1" border="1">
Line 890: Line 891:
 
==Tabbox==
 
==Tabbox==
  
When all your <tab>s and <tabpanel>s (and nested components) have unique IDs everything is simple, but when it comes to dynamically added tabs without nice Ids things get a bit tricky.
+
Quand tous vos <tab's> et <tabpanel's> (et les composants liés) ont des ID's uniques, tout est simple. Mais lorsque les tab's sont ajoutés dynamiquement sans ID's propres, les choses se corsent.
  
So in our example some manual work is required to record interactions with the "comments" tabbox.
+
Dans notre exemple, il faut faire un peu de travail à la main pour enregistrer les interactions avec la tabbox "comments".
  
The "new comment"-button is again a toolbarbutton like the "logout"-button, so its events are not recorded automatically to create a new comment-tab just add this "click zktest=div#newComment_" manually will create a new tab. Now how to activate the new tab (Comment 1).
+
Le bouton "new comment" est encore un bouton de barre d'outils comme le bouton "logout", ses événements ne sont donc pas enregistrés automatiquement pour créer une nouvelle zone de commentaires ajoutez ceci "click zktest=div#newComment_". Comment activer cette nouvelle zone (Comment 1) ?
  
===Locating a Tab===
+
===Localiser une Tab===
  
Automatic recording will give us this, which will work for now, and fail again after a change to the page.
+
L'enregistrement automatique nous donnera ceci, ce qui fonctionne pour le moment mais échouera à nouveau après un changement sur la page.
 
  click zktest=#tab_261-cnt
 
  click zktest=#tab_261-cnt
  
To avoid the hard-coded index, and truly being sure the second tab (inside our comments tabbox) is selected I would prefer this
+
Pour éviter l'index hard-codé et être certain que la deuxième tab (à l'intérieur de notre tabbox de commentaires) est bien sélectionnée, je préfère ceci
  
 
  //div[starts-with(@id, 'comments_')]//li[starts-with(@id, 'tab_')][2]
 
  //div[starts-with(@id, 'comments_')]//li[starts-with(@id, 'tab_')][2]
or (locating by label)
+
ou (locating by label)
 
  //div[starts-with(@id, 'comments_')]//li//span[text() = 'Comment 1']
 
  //div[starts-with(@id, 'comments_')]//li//span[text() = 'Comment 1']
  
It could be simplified increasing the chance of possible future failure (if there is another li inside the "comments" tabbox) using the custom zktest locator
+
On peut simplifier et éviter les risques d'échecs à l'avenir (s'il y a un autre li dans la tabbox "comments") en utilisant le locator zktest
 
  zktest=div#comments_ //li//span[text() = 'Comment 1']
 
  zktest=div#comments_ //li//span[text() = 'Comment 1']
  
===Locating a Tabpanel===
+
===Localiser un Tabpanel===
  
Locating e.g. the <textarea> to edit the comment of the currently selected tab is somewhat more difficult, as the <tabpanels> component is not in the same branch of the Browser-DOM tree as the <tabs> component.
+
Localiser p.e. la <textarea> pour éditer le commentaire de la tab sélectionnée est un peu plus difficile vu que le composant <tabpanels> n'est pas dans la même branche de l'arbre du Browser-DOM que le composant <tabs>.
  
Easiest is to locate by index (if you know it):
+
Il est plus simple de localiser par l'index (si vous le connaissez):
 
  //div[starts-with(@id, 'comments_')]//div[starts-with(@id, 'tabpanel_')][2]//textarea
 
  //div[starts-with(@id, 'comments_')]//div[starts-with(@id, 'tabpanel_')][2]//textarea
or
+
ou
 
  zktest=div#comments_ //div[starts-with(@id, 'tabpanel_')][2]//textarea
 
  zktest=div#comments_ //div[starts-with(@id, 'tabpanel_')][2]//textarea
  
Selecting by label of selected Tab is somewhat complex (I am sure there is a simpler solution to it - if you know feel free to share):
+
Sélectionner par le label du Tab sélectionné (actif) est un peu complexe (Je suis certain qu'une solution plus simple existe - n'hésitez pas à partager si vous en connnaissez une):
 
  xpath=(//div[starts-with(@id, 'comments_')]//textarea[starts-with(@id, 'comment_')])[count(//div[starts-with(@id, 'comments_')]//li[.//span[text() = 'Comment 2']]/preceding-sibling::*)+1]
 
  xpath=(//div[starts-with(@id, 'comments_')]//textarea[starts-with(@id, 'comment_')])[count(//div[starts-with(@id, 'comments_')]//li[.//span[text() = 'Comment 2']]/preceding-sibling::*)+1]
  
Once again we see locators are very flexible - just be creative.
+
Une fois de plus on constate que les locators sont très flexibles, il faut juste être créatif.
  
 
==Combobox==
 
==Combobox==
  
Comboboxes are also very dynamic components. So if you just want to test your page, it is easiest to just "typeAndBlur" a value into them.
+
Les combobox sont aussi des composants très dynamiques. Donc, si vous voulez tester votre page, il est plus simple d'y ajouter simplement à l'intérieur une valeur  "typeAndBlur".
  
 
  typeAndBlur zktest=input#topic_ theValue
 
  typeAndBlur zktest=input#topic_ theValue
  
If you really need to test the <comboitems> are populated correctly, my first suggestion is to test your ListSubModel implementation in a unit test in pure java code. But if you really really need to test this in a page test you can select the combobox-dropdown button with:
+
Si vous voulez tester le fait que les <comboitems> sont correctement peuplés, ma première suggestion est de tester l'implémentation de votre ListSubModel dans un test unitaire codé en java pur. Mais si vous voulez vraiment tester cela dans une page de test, vous pouvez sélectionner le bouton de déroulement de la combobox avec:
  
 
  zktest=i#topic_ /i
 
  zktest=i#topic_ /i
  or
+
  ou
 
  zktest=i#topic_ [contains(@id, '-btn')]
 
  zktest=i#topic_ [contains(@id, '-btn')]
XPath equivalents
+
équivalents XPath  
 
  //i[starts-with(@id, 'topic_')]/i
 
  //i[starts-with(@id, 'topic_')]/i
  or
+
  ou
 
  //i[starts-with(@id, 'topic_')][contains(@id, '-btn')]
 
  //i[starts-with(@id, 'topic_')][contains(@id, '-btn')]
  
To select the "consulting" <comboitem> - via mouse - you could use the following:
+
Pour sélectionner l'item (<comboitem> ) sélectionné par la souris, vous pouvez utiliser ceci:
  
 
<table cellpadding="1" cellspacing="1" border="1">
 
<table cellpadding="1" cellspacing="1" border="1">
Line 961: Line 962:
 
</table>
 
</table>
  
In many cases there is no general recipe, about what is right. In most cases it helps to inspect the page source and do what you need.
+
Dans beaucoup de cas, il n'y pas de recette générale sur ce qui est correct. Il est souvent utile d'inspecter le code source de la page et faire ce dont vous avez besoin.
  
=Putting it all together=
+
=Tout mettre ensemble=
The example package contains a running test suite featuring all the discussed topics from above.
+
Le package exemple contient une suite de tests incluant ceux dont on a parlé au-dessus.
  
It contains 4 test cases. 3 from above and a longer test case "new re-edit  test" showing some more complex component interactions.
+
Il contient 4 cas de tests. 3 discutés ci-avant et un test plus long, "new re-edit  test", qui montre quelques interactions composants plus complexes.
* login test
+
* test de login
* edit save test
+
* test edit save
* new re-edit test
+
* test new re-edit
* logout test
+
* test de logout
  
To see it running you need to:
+
Pour le faire fonctionner, vous devez:
# Unpack the [[#Download|downloaded]] zip file
+
# Décompresser le fichier zip [[#Download|téléchargé]]
# Launch the testing application in test mode (''/selenium_testing_app/launch_for_TEST.bat'' or ''selenium_testing_app/launch_for_TEST.sh'')
+
# Lancer l'application de test en mode "test" (''/selenium_testing_app/launch_for_TEST.bat'' or ''selenium_testing_app/launch_for_TEST.sh'')
# Open the Selenium IDE in Firefox ([CTRL+ALT+S])
+
# Ouvrir Selenium IDE dans Firefox ([CTRL+ALT+S])
# Make sure the extensions are configured (''/extensions/user-extension.js'') - if not, configure them, close and restart Selenium IDE
+
# Vérifier que les extensions sont configurées (''/extensions/user-extension.js'') - si pas, faites le et fermez et redémarrez ensuite Selenium IDE
# In Selenium IDE open the sample test suite (''/test_suite/suite.html'')
+
# Ouvrir dans Selenium IDE la suite de tests (''/test_suite/suite.html'')
# Check the Base URL - should be ''http://localhost:8080/''
+
# Vérifier l'URL de base qui devrait être ''http://localhost:8080/''
# Click the button "Play entire Test Suite"
+
# Cliquer sur le bouton "Play entire Test Suite"
# Lean back and watch...
+
# Vous mettre à l'aise et regarder...
  
=Run against different Browsers using Selenium Server=
+
=Comparer différents navigateurs grâce à Selenium Server=
  
There are many ways of running your tests against different browsers using Selenium Server. As this is not the focus of this document, here are just a few command line examples to see if your suite will actually run outside of Selenium IDE using [http://docs.seleniumhq.org/download/ Selenium Server 2.33] (while writing this document the latest version 2.33 only supports Firefox up to version 21.0)  
+
Il y a plusieurs façon de mener vos tests sur différents navigateurs avec Selenium Server. Vu que ce n'est pas le but de ce document, voici juste quelques lignes de  commandes exemples pour voir si votre suite de tests va fonctionner en dehors de Selenium IDE en utilisant [http://docs.seleniumhq.org/download/ Selenium Server 2.33] (au moment de l'écriture de ce document, la dernière version 2.33 ne supporte Firefox que jusque la version 21.0)  
  
Place the ''selenium-server-standalone-2.33.0.jar'' in the folder where you unzipped this example and run a command prompt at the same path:
+
Placez le ''selenium-server-standalone-2.33.0.jar'' dans le dossier où vous avez décompressé l'exemple et démarrez un prompt de commande au même endroit (même chemin):
(of course you might have to adapt the paths of your browser executables)
+
(il faut peut-être bien entendu adapter les chemins des exécutables de vos navigateurs)
  
execute with Firefox '''21.0''':
+
Avec Firefox '''21.0''':
 
  java -jar selenium-server-standalone-2.33.0.jar -userExtensions extensions/user-extensions.js -htmlSuite "*firefox C:\Program Files (x86)\Mozilla Firefox 21\firefox.exe" "http://localhost:8080" test_suite/suite.html test_suite/results.html
 
  java -jar selenium-server-standalone-2.33.0.jar -userExtensions extensions/user-extensions.js -htmlSuite "*firefox C:\Program Files (x86)\Mozilla Firefox 21\firefox.exe" "http://localhost:8080" test_suite/suite.html test_suite/results.html
  
execute with google chrome:
+
Avec Google Chrome:
 
  java -jar selenium-server-standalone-2.33.0.jar -userExtensions extensions/user-extensions.js -htmlSuite "*googlechrome C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" "http://localhost:8080" test_suite/suite.html test_suite/results.html
 
  java -jar selenium-server-standalone-2.33.0.jar -userExtensions extensions/user-extensions.js -htmlSuite "*googlechrome C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" "http://localhost:8080" test_suite/suite.html test_suite/results.html
  

Latest revision as of 06:49, 23 April 2014

DocumentationSmall Talks2013JulyZK Testing with Selenium IDE fr
ZK Testing with Selenium IDE fr

Author
Robert Wenzel, Engineer, Potix Corporation
Date
July 30, 2013
Version
ZK 6.5 (or later)


Introduction

Vous avez donc votre chouette application ZK et vous aimeriez maintenant être de certain qu'elle va fonctionner correctement sur différents navigateurs (et continuera de fonctionner après de changements, ce qui survient souvent de nos jours...). Vous avez bien entendu testé tout ce qui était possible avec votre test unitaire et des tests d'intégration ;-). Vous avez essayé toutes les possibilités offertes par ZATS pour avoir la certitude que vos composants ZK fonctionnent correctement ensemble dans un environnement simulé.

Vous en arrivez donc finalement au point de vouloir garantir le bon fonctionnement de votre application (ou au moins les éléments essentiels de celle-ci) dans un environnement réel (avec d'autres navigateurs, serveur réel d'applications, trafic et latence réseaux réels etc.) - alors, Selenium entre en jeux.

À propos de cet article

Différentes approches existent pour créer des tests avec Selenium:

Par programmation - méthode préférée des développeurs - est déjà développée dans ce Small Talk. Cette méthode vous offre un contrôle maximum sur l'exécution du test et vous permet d'écrire des tests réutilisables, flexibles, facile à mettre à jour et même des cas "intelligents". Bien sure, pour écrire de tels tests, il faut du temps et des ressources.

Il arrive parfois qu'une personne ayant un profil moins technique doive écrire ces tests. On entend alors souvent parler d'outils "record and replay". Selenium IDE possède cette caractéristique "record and replay" mais aussi beaucoup d'autres options adapter et étendre les tests après enregistrement. Dans de raraes cas, les tests enregistrés peuvent être rejoués de façon instantanée. Cependant, particulièrement avec AJAX, certains éléments vont permettre d'éviter l'exécution de vos tests hors carde.

Voici deux exemples (et vous pouvez facilement en trouver d'autres):

  • problèmes avec Missing clicks
  • quel'qu'un a enregistré ceci video qui explique quelques problèmes

Mais pas de panique, cet article va vous expliquer comment gérer ces cas!

La conclusion majeure est: Voyez plutôt Selenium IDE comme un outil d'enregistrement/adaptation/maintenance et de ré-exécution/débogage lorsque vous construisez vos tests.

Cet article va montrer (via une application web exemple) comment:

  • autoriser un enregistrement de test initial dans ZK
  • adapter les tests pour pouvoir les rejouer
  • garder vos tests maintenables et robustes face aux changements dans vos pages
  • ajouter les actions manquantes

Nombre d'idées dans cet article sont basées sur des solutions à des problèmes généraux depuis certains forums et aussi l'expérience des projets passés. Certaines sont simplement le reflet de mes préférences personnelles sur la manière de gérer les choses. Cet article ne montre pas une manière unique d'écrire des tests pour des applications ZK mais met juste en évidence des problèmes et des solutions, discute de certains considérations prises, et explique pourquoi cette approche a été choisie.

Bonne lecture et n'hésitez pas à commenter !!

Environment

  • Firefox 22
  • Selenium IDE 2.1.0 (Selenium IDE 2.0.0 ne fonctionne pas avec FF >=22)
  • JDK 6/7
  • ZK 6.5.3
  • Maven 3.0

Étapes

Initialisez votre environnement pour cet exemple

  • Installez le plugin Selenium IDE dans Firefox via Addons ou download documentation page
  • Maven 3.0 download & install
  • download le projet exemple, il contient les dossiers suivants:
    • selenium_example/ (l'application exemple, le projet maven)
    • extensions/ (les extension selenium utilisées dans cet article)
    • test_suite/ (une suite de tests Selenium illustrant les concepts discutés ici)
  • lancez depuis une ligne de commande
cd selenium_example_app
mvn jetty:run
ou exécutez selenium_testing_app/launch_for_PROD.bat / .sh

Situation initiale

  1. Lancez Firefox
  2. Lancez Selenium IDE CTRL+ALT+S
  3. Mettez l'URL de base à "http://localhost:8080/"
  4. Ouvrez l'application exemple http://localhost:8080/selenium_testing
  5. Enregistrez le login-test et essayez de le ré-exécuter
New Test
open /selenium_testing/login.zul
type id=jXDW5 test
type id=jXDW8 test
clickAndWait id=jXDWb

Cela parait difficile à lire / maintenir et ne fonctionne même pas :'(

En jetant un coup d’œil aux commandes enregistrées et en comparant les IDs à la page source, nous remarquons que ces derniers sont générés et changés à chaque rafraîchissement de la page. Aucune chance donc d'enregistrer / rejouer de cette manière.

Une manière de rendre les IDs prévisibles est montrée dans la section suivante.

Générateur d'ID sur mesure

Comme mentionnée dans l'autre Small Talks sur les tests, une stratégie est d'utiliser une implémentation d'un générateur d'ID sur mesure pour créer des ID's de composants qui soient prévisibles, lisibles (avec un nom business) et facilement sélectionnable par selenium.

public class TestingIdGenerator implements IdGenerator {

	public String nextComponentUuid(Desktop desktop, Component comp,
			ComponentInfo compInfo) {
        int i = Integer.parseInt(desktop.getAttribute("Id_Num").toString());
        i++;// Start from 1
        
        StringBuilder uuid = new StringBuilder("");
        
        desktop.setAttribute("Id_Num", String.valueOf(i));
        if(compInfo != null) {
        	String id = getId(compInfo);
        	if(id != null) {
        		uuid.append(id).append("_");
        	}
        	String tag = compInfo.getTag();
        	if(tag != null) {
        		uuid.append(tag).append("_");
        	}
        }
        return uuid.length() == 0 ? "zkcomp_" + i : uuid.append(i).toString();
    }

Les IDs ressembleront à ceci:

  • {id}_{tag}_{##} (pour les composants avec un id donné)
  • {tag}_{##} (pour les composants sans id donnée - p.e. listitems dans une listbox)
  • {zkcomp}_{##} (pour les autres cas, histoire simplement de les rendre uniques)

p.e. un bouton dans un fichier zul

<button id="login" label="login" />

deviendra quelque chose comme ceci en HTML dans un navigateur (le nombre peut varier):

<button id="login_button_12" class="z-button-os" type="button">login</button>

Pour séparer la production de la configuration test, on peut inclure un ficher de config supplémentaire au démarrage pour autoriser le TestingIdGenerator uniquement dans les tests. Avec Zk, ceci est possible en configurant la propriété de librairies org.zkoss.zk.config.path (voir aussi notre Testing Tips)

p.e. via l'arguement VM -Dorg.zkoss.zk.config.path=/WEB-INF/zk-test.xml

Windows:

set MAVEN_OPTS=-Dorg.zkoss.zk.config.path=/WEB-INF/zk-test.xml
mvn jetty:run

Linux:

export MAVEN_OPTS=-Dorg.zkoss.zk.config.path=/WEB-INF/zk-test.xml
mvn jetty:run

ou exécutez selenium_testing_app/launch_for_TEST.bat / .sh


On a maintenant des ID's déterminés lors de l'enregistrement d'un test.

Après l'enregistrement, on relance le test et on obtient ceci:

login test
open /selenium_testing/login.zul
type id=username_textbox_6 test
type id=password_textbox_9 test
clickAndWait id=login_button_12

Ça parait déjà mieux et ça s'explique de sois-même, mais malheureusement cela no fonctionne toujours pas si on le relance.

Pourquoi cela se passe-t-il et comment le régler ? Voyons la section suivante ...

Détails spécifiques à ZK

Vu la nature des applications web riches, JavaScript est souvent très utilisé mais Selenium IDE n'enregistre pas par défaut tous les événements. Dans notre cas, les événements "blur" des champs d'entrée n'ont pas été enregistrés alors que ZK se base sur eux pour déterminer les champs mis à jour. On doit donc ajouter manuellement la commande selenium "fireEvent target blur". Selenium n'offre malheureusement pas d'équivalente à "focus".

login test
open /selenium_testing/login.zul
type id=username_textbox_6 test
fireEvent id=username_textbox_6 blur
type id=password_textbox_9 test
fireEvent id=password_textbox_9 blur
clickAndWait id=login_button_12
assertLocation */selenium_testing/index.zul
verifyTextPresent Welcome test
verifyTextPresent Feedback Overview

Si on relance le script, maintenant cela fonctionne :D et on voit la page overview (on a aussi ajouté quelques vérifications pour cela).

Extensions Selenium

Si vous avez le sentiment qu'il est difficile de se souvenir de la syntaxe de "fireEvent" ou si vous pensez qu'il est trop compliqué d'ajouter une ligne additionnelle juste pour mettre à jour un champ d'entrée, utiliser une Selenium Core extension peut vous aider. Le fichier d'extension, généralement appelé user-extensions.js peut être utilisé par Selenium IDE et par Selenium RC lorsque les tests se font en dehors de Selenium IDE (merci de vous référer à selenium documentation).

L'extrait suivant montrent deux actions simples:

blur
pratique pour éviter "fireEvent locator blur"
typeAndBlur
combine le type avec un événement blur, pour rendre ZK attentif aux changement de la valeur entrée
Selenium.prototype.doBlur = function(locator) {
    // All locator-strategies are automatically handled by "findElement"
    var element = this.page().findElement(locator);
    // Fire the "blur" event
    this.doFireEvent(locator, "blur")
};

Selenium.prototype.doTypeAndBlur = function(locator, text) {
    // All locator-strategies are automatically handled by "findElement"
    var element = this.page().findElement(locator);

    // Replace the element text with the new text
    this.page().replaceText(element, text);
    // Fire the "blur" event
    this.doFireEvent(locator, "blur")
};

Pour l'activer, ajoutez le fichier (extensions/user-extension.js dans le zip de l'exemple) à la configuration de Selenium IDE. (le fichier contient une autre extension ... voir custom locators)

(Selenium IDE Options > Options... > [General -Tab] > Selenium Core extensions)

Redémarrez ensuite Selenium IDE - fermez la fenêtre et rouvrez-la - p.e. avec [CTRL+ALT+S]

Test adapté qui utilise l'action blur:

login test
open /selenium_testing/login.zul
type id=username_textbox_6 test
blur id=username_textbox_6
type id=password_textbox_9 test
blur id=password_textbox_9
clickAndWait id=login_button_12

Même test en utilisant typeAndBlur:

login test
open /selenium_testing/login.zul
typeAndBlur id=username_textbox_6 test
typeAndBlur id=password_textbox_9 test
clickAndWait id=login_button_12

Il n'est pas nécessaire d'utiliser l'une de ces extensions mais ma préférence personnelle va à l'économie d'une ligne pour chaque entrée.

Une autre chose à garder à l'esprit est le manque de robustesse et l'effort de maintenance à fournir pour les tests quand des changements apparaissent dans l'UI ... les numéros générés à la fin de chaque ID restent un obstacle parce qu'ils sont sujets à changer chaque fois qu'un composant est ajouté ou retiré au dessus de ce composant.

Ne serait-il pas sympa de n'avoir à adapter aussi souvent les tests ? Voyons la suite...

Améliorer la robustesse et la maintenance

Locators dans Selenium

Pour enlever les nombres hard codés des ID composants de notre test, Selenium offre p.e. XPath ou CSS locators.

En utilisant XPath, est-il possible de sélectionner un nœud à l'aide de son préfixe ID ?:

//input[starts-with(@id, 'username_')]

Cela va fonctionner tant que le préfixe "username_" est unique sur la page, sans quoi l'action sera exécutée sur le premier élément trouvé. C'est donc une bonne idée, dans ce scénario, de donner aux widgets des ID appropriés (plus: cela va aussi augmenter la lisibilité du code source).

Dans des cas plus complexes, on peut sélectionner des composants imbriqués pour distinguer les composants avec le même ID (p.e. dans des espaces ID différents). p.e. si le composant "street" apparaît plusieurs fois sur la page, utilisez ceci:

//div[starts-with(@id, 'deliveryAddress_')]//input[starts-with(@id, 'street_')]
//div[starts-with(@id, 'billingAddress_')]//input[starts-with(@id, 'street_')]

Un autre exemple pour localiser le bouton "delete" dans la ligne sélectionnée d'une listbox qui utilise des préfixes ID, CSS-class et comparaison de texte.

//div[starts-with(@id, 'overviewList_')]//tr[contains(@class, 'z-listitem-seld')]//button[text() = 'delete']

NOTE: Veillez à ne pas utiliser l'opérateur "//" de façon trop extensive dans XPath, cela pourrait ne pas fonctionner (cela n'a jamais été un problème pour moi jusqu’à présent vu qu'il y a des goulots d’étranglement bien pire en terme de performance qui affectent la vitesse d'exécution de votre test - voir plus tard).

Ce lien sheet provided by Michael Sorens est un excellente référence avec plein d'exemples utiles sur comment sélectionner des éléments de page dans diverses situations.

On peut donc maintenant changer le test pour utiliser cette sorte de XPath locator, en cherchant uniquement avec le préfixe ID:

login test
open /selenium_testing/login.zul
typeAndBlur //input[starts-with(@id, 'username_')] test
typeAndBlur //input[starts-with(@id, 'password_')] test
clickAndWait //button[starts-with(@id, 'login_')]

Ceci fait, l'ordre des composants peut changer sans casser le test, tant que les IDs ne sont pas changés. Ceci requirt bien entendu un peu de travail manuel mais l'effort investi une fois payera rapidement au cours de l'évolution des votre projet.

Custom Locator et LocatorBuilder

Si vous ne voulez pas changer le locator vers XPath de façon manuelle à chaque fois que vous enregistre un test, Selenium IDE vous offre une solution qui va encore augmenter la lisibilité.

Custom Locator

Dans le user-extensions.js d'ici plus haut, vous allez trouver un custom locator qui va cacher une partie de la complexité de XPath pour des scénarios simples. C'est basé sur le format des IDs générés par TestingIdGenerator (voir plus haut).
// The "inDocument" is the document you are searching.
PageBot.prototype.locateElementByZkTest = function(text, inDocument) {
    // Create the text to search for
	
    var text2 = text.trim();

	var separatorIndex = text2.indexOf(" ");
	
	var elementNameAndIdPrefix = (separatorIndex != -1 ? text2.substring(0, separatorIndex) : text2).split("#");
	var xpathSuffix = separatorIndex != -1 ? text2.substring(separatorIndex + 1) : "";
	
	var elementName = elementNameAndIdPrefix[0] || "*";
	var idPrefix = elementNameAndIdPrefix[1];

	var xpath = "//" + elementName + "[starts-with(@id, '" + idPrefix + "')]" + xpathSuffix;

    return this.xpathEvaluator.selectSingleNode(inDocument, xpath, null, this._namespaceResolver);
};

Ce locator va travailler selon 2 formes

1. zktest=#login_
2. zktest=input#username_

XPaths généré/questionné en interne sera

1. //*[starts-with(@id, 'login_')]
2. //input[starts-with(@id, 'username_')]
  1. cherchera les éléments dont l'ID commence par "login_"
  2. testera aussi les éléments avec tag html, et trouvera l'élément <input> avec le préfixe ID "username_..."

De plus, il est possible de joindre n'importe quel XPath supplémentaire pour affiner les résultats.

p.e. (pour localiser la deuxième option d'un élement <select>)

zktest=select#priority_ /option[2]

deviendra dans XPath

//select[starts-with(@id, 'priority_')]/option[2]

Même si ce locator est très basique (peut être étendu si besoin), il augmente déjà la lisibilité de nombreux use-cases.

Custom LocatorBuilder

Pour rendre ceci très maniable, Selenium IDE offre aussi des extensions (ne pas mélanger avec Selenium Core Extension)

Ajoutez le ficher extension IDE extensions/zktest-Selenium-IDE-extension.js depuis example.zip à la config selenium

(Selenium IDE Options > Options... > [General -Tab] > Selenium IDE extensions)

et déplacez le vers le haut dans la liste de priorités du Locator Builder.

(Selenium IDE Options > Options... > [Locator Builders - Tab])

Il contient le LocatorBuilder suivant qui va générer le locator dans la forme "zktest=elementTag#IdPrefix" mentionnée ci-dessus en utilisant uniquement la première partie de l'ID (le préfixe ID - son nom business). Si un ID ne contient pas 3 tokens séparés par "_", il utilisera l'ID complet à la plac en incluant le numéro séquentiel. Ceci indiquera que l'élément n'a pas d'ID spécifié et une autre approche de localisation peut être meilleure, ou donnez lui juste un ID dans le fichier zul.

 
LocatorBuilders.add('zktest', function(e) {
	var idTokens = e.id.split("_")
	if (idTokens.length > 2) {
		idTokens.pop();
		idTokens.pop();
		return "zktest=" + e.nodeName.toLowerCase() + "#" + idTokens.join("_") + "_";
	} else {
		return "zktest=" + "#" + e.id;
	}
	return null;
});

Redémarrez Selenium IDE et enregistrez le test à nouveau... vous aurez ceci (après avoir changé les événements de type à typeAndBlur).

New Test
open /selenium_testing/login.zul
typeAndBlur zktest=input#username_ test
typeAndBlur zktest=input#password_ test
clickAndWait zktest=button#login_

Plus de Tests

Faisons un test pour l'édition et la sauvegarde d'un "feedback item" existant dans la liste, vérifions les résultats et faisons un test de lougout. Ces tests introduisent des nouveaux challenges pour tester une application ajax avec Selenium - et leurs solutions ou contournements.

Maintenant que nous changeons les données, j'ai adapté le test de login pour utiliser un utilisateur aléatoire à chaque fois. Dès lors, l'implémentation Mock fournira des données fraîches à chaque essai, sans avoir à redémarrer le serveur ni à faire de nettoyage (il existe d'autres stratégies pour faire la même chose, c'est simplement celle que j'utilise ici).

login test
store javascript{'testUser'+Math.floor(Math.random()*10000000)} username
open /selenium_testing/login.zul
typeAndBlur zktest=input#username_ ${username}
typeAndBlur zktest=input#password_ ${username}
clickAndWait zktest=button#login_
assertLocation */selenium_testing/index.zul
verifyTextPresent logout ${username}

Test Edit et Save

Après avoir enregistré et appliqué les actions typeAndBlur, on obtient ceci:

edit save test
open /selenium_testing/index.zul
click zktest=#button_27
typeAndBlur zktest=input#subject_ Subject 1 (updated)
typeAndBlur zktest=input#topic_ consulting
select zktest=select#priority_ label=low
click zktest=#listitem_62
typeAndBlur zktest=textarea#comment_ test comment (content also updated)
click zktest=button#submit_
assertTextPresent Feedback successfully updated
verifyTextPresent subject=Subject 1 (updated)
verifyTextPresent [text=test comment (content also updated)]
verifyTextPresent priority=low
click zktest=button#backToOverview_
assertTextPresent Feedback Overview

Nous voyons toujours quelques valeurs hard-codées. Je vais juste supprimer "click zktest=#listitem_62" vu que la commande "select" fera l'affaire (Selenium IDE va parfois enregistrer plus que nécessaire, et parfois pas assez - par défaut).

Ce qui est intéressant maintenant est le locator "zktest=#button_27" - un des boutons "edit". Si nous essayons de lui donner un ID dans le code zul, cela ne donnera aucun résultat (ID dupliqué) pcq le bouton est déclaré plusieurs fois - à chaque <listitem>. (un contournement possible serait de l'encadrer avec un composant <idspace> ou d'inclure les boutons depuis un fichier différent). Mais cela n'est pas nécessaire - en utilisant le bon locator XPath.

Localiser un élement dans une Listbox

Si vous voulez localiser le premier bouton edit dans une liste, vous pouvez utiliser(il trouvera le bouton par son texte et non son ID):

pure XPath:

//*[starts-with(@id, 'overviewList_')]//button[text() = 'edit']

ou combiner avec le selecteur "zktest" de l'extension selenium vue précédemment

zktest=#overviewList_ //button[text() = 'edit']

Il est plus intéressant de localiser par l'index, p.e. exactement le deuxième bouton edit

xpath=(//*[starts-with(@id, 'overviewList_')]//button[text() = 'edit'])[2]

ou plus marrant de sélectionner le bouton "edit" dans un <listitem> contenant un texte spécifique dans une cellule

xpath=//*[starts-with(@id, 'overviewList_')]//*[starts-with(@id, 'listitem_')][.//*[starts-with(@id, 'listcell_')][text() = 'Subject 2']]//button[text() = 'edit']

Cela peut devenir très complexe et diminuer la lisibilité de votre test. C'est donc toujours une bonne idée d'écrire des comments (voir screenshot en dessous) dans vos test (oui, ils peuvent aussi avoir des commentaires).

Bien que compliqué, ce dernier locator reste stable et fait face aux changements de l'UI. Si le contenu de <listbox> changes, vous pouvez toujours l'utiliser pour trouver le <listitem> (et le bouton qu'il contient) plusieurs fois au cours d'un test.

Utiliser des variables

Maintenir ces locators dans vos scripts de tests va vous donner la migraine, p.e. si le label d'un bouton change de "edit" à "update". Il est plus simple de "stocker" les chaines de caractères, ou une partie de celles-ci, dans des variables qui peuvent être réutilisées tout au long du test.

variables and comments example

Ce ne sont que quelques idées générales sur comment réduire le travail de maintenance des tests, en augmentant leur stabilité ou en les rendant plus lisibles - moyennant un investissement raisonnable lors de la mise sur pied initiale.

Attendre AJAX

Bien que la ré-exécution du test à pleine vitesse (ce qui est toujours l'objectif) va, ou pourrait parfois, échouer (pcq on attend pas assez longtemps les réponses d'ajax)... il n'y a pas de rechargement complet de page, donc Selenium IDE n'attend pas de manière automatique.

Une idée serait de réduire la vitesse du test... ce qui affecterait chaque étape dans tous les tests, nous essayons donc d'éviter cela. Aussi, dans notre exemple de feedback, l'opération du click sur le bouton "submit" est longue (2 secondes). Retarder chaque étape de 2 secondes provoquerait des dégâts considérables sur la durée de notre replay global.

Un autre possibilité est d'utiliser la commande pause et spécifier le nombre de millisecondes à attendre. Ici aussi la vitesse d'exécution peut varier et nous serons donc trop court, ce qui va engendrer des erreurs, ou trop longs ("juste pour être certain") et gaspiller du temps, ces deux effets étant indésirables.

Heureusement, Selenium fournit une variété d'actions du type waitFor... qui interrompent l'exécution tant qu'une condition n'est pas rencontrée. Une section très utile:

  • waitForTextPresent - attend q'un texte soit présent sur le page
  • waitForText - attend qu'un élément reçoive un texte
  • waitForElementPresent - attend qu'une élément soit présenté
  • waitForVisible - attend qu'un élément devienne visible

Le test "edit et save" précédent devrait échouer à la ligne 3 juste après le click sur le bouton "edit". Entre l'étape 2 et l'étape 3, ZK met à jour la page avec des éléments d'AJAX, ce qui provoque un délais court (mais long assez que pour faire échouer le test - lorsqu'il est à pleine vitesse).

edit save test
open /selenium_testing/index.zul
click zktest=#overviewList_ //button[text() = 'edit']
typeAndBlur zktest=input#subject_ Subject 1 (updated)
typeAndBlur zktest=input#topic_ consulting
select zktest=select#priority_ label=low
typeAndBlur zktest=textarea#comment_ test comment (content also updated)
click zktest=button#submit_
assertTextPresent Feedback successfully updated
... ... ...

On doit donc attendre p.e. que le nouveau Headline "New/Edit Feedback" soit présent, ce qui indique que la mise à jour AJAX est terminée. Et attendre en conséquence après l'envoi de l'article de feedback - remplacer "assertTextPresent" par "waitForTextPresent".

edit save test
open /selenium_testing/index.zul
click zktest=#overviewList_ //button[text() = 'edit']
waitForTextPresent New/Edit Feedback
typeAndBlur zktest=input#subject_ Subject 1 (updated)
typeAndBlur zktest=input#topic_ consulting
select zktest=select#priority_ label=low
typeAndBlur zktest=textarea#comment_ test comment (content also updated)
click zktest=button#submit_
waitForTextPresent Feedback successfully updated
verifyTextPresent subject=Subject 1 (updated)
verifyTextPresent [text=test comment (content also updated)]
verifyTextPresent priority=low
click zktest=button#backToOverview_
waitForTextPresent Feedback Overview

Ce test peut maintenant fonctionner aussi vite que possible, s'adapter automatiquement à une quelconque augmentation ou diminution de vitesse du serveur de test.

Il est aussi possible d'attendre de façon implicite qu'AJAX ait fini à un niveau technique [traité ici]. Je pense personnellement que c'est tentant au premier abord. Mais après réflexion, je préfère l'idée d'une attente explicite (via waitForXXX) - seulement quand il faut attendre.

Vous pourez aussi trouver par vous-même quelque-chose sur votre page qui affecte la performance (= expérience utilisateur) - si un test échoue soudainement après un changement qui affecte la capacité de réponse de votre application.

De plus, il pourrait y avoir un autre délais entre le moment ou AJAX a terminé et le moment où le composant affecté par cette réponse est réellement visible sur la page(p.e. à cause d'une animation). Cette approche pourrait donc toujours échouer - à garder en mémoire.

Test Logout

Ce test devrait être très simple, malgré tout il y a une attrape. En essayant d'enregistrer ce test, nous remarquons que Seleniume-IDE n'enregistre pas le click sur le boutone logout. Il est difficile de prévoir quels événements Selenium IDE va enregistrer, et quels sont ceux qu'il va ingorer. Dans ce cas-ci, c'est un <toolbarbutton> qui est présenté comme <div> c'est pet-être la raison.

(Voir [ici] et aussi [une extension IDE] disponible pour permettre l'enregistrement de tous les clicks lors d'un test. J'ai essayé celui-ci - et n'ai pas aimé le grand nombre de clicks enregistrés. J'ai finalement laissé ceci extension IDE pour Locator Builder founi ci-dessus et jugez vous-même - vous désirez peut-être tester exactement cela.)

Vu que nous connaissons son nom (ou on peut facilement le trouver avec un outil comme firebug), on ajoute simplement un autre événement click manuellement et nous changeons le locator à "zktest=div#logout_"

logout test
click zktest=div#logout_
waitForTextPresent Login (with same username as password)
assertLocation */selenium_testing/login.zul

Exemples avec d'autres composants

D'autres composants requièrent aussi une attention particulière lorsqu'on enregistre les événements dessus (vous pourriez vous demandez parfois pourquoi les résultats changent). Voici donc quelques exemples.

Bouton (mold="trendy")

La page "page new/edit" de l'exemple contient 2 boutons d'envoi - leurs rendus utilisent des moules différents: "default" et "trendy". Tous les deux ont le même effet (sauvegarder l'élément de feedback). Lorsque vous essayer d'enregistrer l'événement click du deuxième - labellisé "submitTrendy" - Selenium IDE ingore complètement le click.

Et la chose marrante est lorsque vous mettez d'abord le focus dans une autre fenêtre et cliquez ensuite sur le bouton directement - sans remettre d'abord le focus dans la fenêtre-, il est enregistré. Mais notre locator builder ne correspond pas ici. On obtient donc d'autres chaines de caractère de locator génériques. (Le bouton "trendy" est présenté avec l'aide d'un tableau de layout et des images pour les coins arrondis.)

button trendy recorded locator

Le second choix contient l'ID du composant, nous pouvons l'utiliser pour construire notre propre chaîne de caractères de locator.

zktest=#submitTrendy_

Ceci serait généralement suffisant, mais pas dans cet exemple. Au cours du test, lorsqu'on clique sur le bouton "Find" dans Selenium IDE, on le voit sélectionner la cellule entière autour de celui-ci. ZK a généré quelques éléments html de la grille de layout autour de notre bouton et partageant le même préfixe ID. En inspectant l'élément dans le navigateur, on voit que l'élément sur lequel on souhaite cliquer réellement est un élément avec une classe de style "z-button"

<span id="submitTrendy_button_129" class="z-button">

Un locator plus spécifique ressemblerait à ceci

zktest=span#submitTrendy_

ou à cela

zktest=#submitTrendy_ [@class = "z-button"]

Les XPaths équivalents sont:

//span[starts-with(@id, 'submitTrendy_')]
//*[starts-with(@id, 'submitTrendy_')][@class="z-button"]

Menu

Enregistrer les événements sur une <barre de menu> (même avec des sous-menus) est généralement très simple. À moins que vous ne cliquiez la "mauvaise" zone lors de l'enregistrement(voir en rouge sur l'image en dessous). Si vous cliquez juste sur les menu(item)-textes (en vert), Selenium IDE va créer des bons locators, sans quoi il faut passer à d'autres stratégies utilisant CSS ou des XPath générés qui pourraient vous surprendre (ce n'est pas un bug de Selenium IDE, c'est juste un élément html différent qui reçoit l'événement click).

menu test (not working for us)
click css=td.z-menu-inner-m > div
click css=span.z-menuitem-img

avoid red areas, and click on the green areas

Ce que vous obtiendrez en évitant les zones "rouges".

menu test (what we want)
click zktest=button#feedbackMenu_
click zktest=a#newFeedbackMenuItem_

Tabbox

Quand tous vos <tab's> et <tabpanel's> (et les composants liés) ont des ID's uniques, tout est simple. Mais lorsque les tab's sont ajoutés dynamiquement sans ID's propres, les choses se corsent.

Dans notre exemple, il faut faire un peu de travail à la main pour enregistrer les interactions avec la tabbox "comments".

Le bouton "new comment" est encore un bouton de barre d'outils comme le bouton "logout", ses événements ne sont donc pas enregistrés automatiquement pour créer une nouvelle zone de commentaires ajoutez ceci "click zktest=div#newComment_". Comment activer cette nouvelle zone (Comment 1) ?

Localiser une Tab

L'enregistrement automatique nous donnera ceci, ce qui fonctionne pour le moment mais échouera à nouveau après un changement sur la page.

click zktest=#tab_261-cnt

Pour éviter l'index hard-codé et être certain que la deuxième tab (à l'intérieur de notre tabbox de commentaires) est bien sélectionnée, je préfère ceci

//div[starts-with(@id, 'comments_')]//li[starts-with(@id, 'tab_')][2]

ou (locating by label)

//div[starts-with(@id, 'comments_')]//li//span[text() = 'Comment 1']

On peut simplifier et éviter les risques d'échecs à l'avenir (s'il y a un autre li dans la tabbox "comments") en utilisant le locator zktest

zktest=div#comments_ //li//span[text() = 'Comment 1']

Localiser un Tabpanel

Localiser p.e. la <textarea> pour éditer le commentaire de la tab sélectionnée est un peu plus difficile vu que le composant <tabpanels> n'est pas dans la même branche de l'arbre du Browser-DOM que le composant <tabs>.

Il est plus simple de localiser par l'index (si vous le connaissez):

//div[starts-with(@id, 'comments_')]//div[starts-with(@id, 'tabpanel_')][2]//textarea

ou

zktest=div#comments_ //div[starts-with(@id, 'tabpanel_')][2]//textarea

Sélectionner par le label du Tab sélectionné (actif) est un peu complexe (Je suis certain qu'une solution plus simple existe - n'hésitez pas à partager si vous en connnaissez une):

xpath=(//div[starts-with(@id, 'comments_')]//textarea[starts-with(@id, 'comment_')])[count(//div[starts-with(@id, 'comments_')]//li[.//span[text() = 'Comment 2']]/preceding-sibling::*)+1]

Une fois de plus on constate que les locators sont très flexibles, il faut juste être créatif.

Combobox

Les combobox sont aussi des composants très dynamiques. Donc, si vous voulez tester votre page, il est plus simple d'y ajouter simplement à l'intérieur une valeur "typeAndBlur".

typeAndBlur zktest=input#topic_ theValue

Si vous voulez tester le fait que les <comboitems> sont correctement peuplés, ma première suggestion est de tester l'implémentation de votre ListSubModel dans un test unitaire codé en java pur. Mais si vous voulez vraiment tester cela dans une page de test, vous pouvez sélectionner le bouton de déroulement de la combobox avec:

zktest=i#topic_ /i
ou
zktest=i#topic_ [contains(@id, '-btn')]

équivalents XPath

//i[starts-with(@id, 'topic_')]/i
ou
//i[starts-with(@id, 'topic_')][contains(@id, '-btn')]

Pour sélectionner l'item (<comboitem> ) sélectionné par la souris, vous pouvez utiliser ceci:

New Test
click zktest=i#topic_ /i
waitForVisible zktest=div#topic_ /table
click zktest=div#topic_ //tr[starts-with(@id, 'comboitem_')]//td[text() = 'consulting']

Dans beaucoup de cas, il n'y pas de recette générale sur ce qui est correct. Il est souvent utile d'inspecter le code source de la page et faire ce dont vous avez besoin.

Tout mettre ensemble

Le package exemple contient une suite de tests incluant ceux dont on a parlé au-dessus.

Il contient 4 cas de tests. 3 discutés ci-avant et un test plus long, "new re-edit test", qui montre quelques interactions composants plus complexes.

  • test de login
  • test edit save
  • test new re-edit
  • test de logout

Pour le faire fonctionner, vous devez:

  1. Décompresser le fichier zip téléchargé
  2. Lancer l'application de test en mode "test" (/selenium_testing_app/launch_for_TEST.bat or selenium_testing_app/launch_for_TEST.sh)
  3. Ouvrir Selenium IDE dans Firefox ([CTRL+ALT+S])
  4. Vérifier que les extensions sont configurées (/extensions/user-extension.js) - si pas, faites le et fermez et redémarrez ensuite Selenium IDE
  5. Ouvrir dans Selenium IDE la suite de tests (/test_suite/suite.html)
  6. Vérifier l'URL de base qui devrait être http://localhost:8080/
  7. Cliquer sur le bouton "Play entire Test Suite"
  8. Vous mettre à l'aise et regarder...

Comparer différents navigateurs grâce à Selenium Server

Il y a plusieurs façon de mener vos tests sur différents navigateurs avec Selenium Server. Vu que ce n'est pas le but de ce document, voici juste quelques lignes de commandes exemples pour voir si votre suite de tests va fonctionner en dehors de Selenium IDE en utilisant Selenium Server 2.33 (au moment de l'écriture de ce document, la dernière version 2.33 ne supporte Firefox que jusque la version 21.0)

Placez le selenium-server-standalone-2.33.0.jar dans le dossier où vous avez décompressé l'exemple et démarrez un prompt de commande au même endroit (même chemin): (il faut peut-être bien entendu adapter les chemins des exécutables de vos navigateurs)

Avec Firefox 21.0:

java -jar selenium-server-standalone-2.33.0.jar -userExtensions extensions/user-extensions.js -htmlSuite "*firefox C:\Program Files (x86)\Mozilla Firefox 21\firefox.exe" "http://localhost:8080" test_suite/suite.html test_suite/results.html

Avec Google Chrome:

java -jar selenium-server-standalone-2.33.0.jar -userExtensions extensions/user-extensions.js -htmlSuite "*googlechrome C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" "http://localhost:8080" test_suite/suite.html test_suite/results.html

Appendix

Download

selenium-IDE-example.zip

Comments



Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License.