Testez vos applications graphiques web

Dans les applications de taille importante, les modifications des pages web et la nécessité de compatibilité avec différents navigateurs rendent les tests web fastidieux. Il existe de nombreux outils pour faciliter cette tâche :

  • StrutsTestCase, une extension de la classe JUnit TestCase afin de tester la couche controller des application Struts. Il permet notamment de tester les forwards, les messages d’erreurs générés en fonction de ce qui est entré dans les ActionForm.
  • HtmlUnit
  • JWebUnit
  • Selenium

et bien d’autres encore (Fitness, Jqunit …)

 HtmlUnit, JWebUnit et Selenium contrairement à StrutsTestCase simule directement les actions des utilisateurs (clics sur des boutons, des liens, radio bouton, liste déroulante …, entrée dans des input text, soumission de formulaire, appel de code javascript …) dans des navigateurs réels ou virtuels.

HtmlUnit (licence Apache 2) simule en mémoire un browser et fournit une API riche pour décrire en java un scénario d’utilisation d’une application dans un  navigateur. Junit ou TestNG sont utilisables pour faire des assertions sur le contenu de la page HTML. HtmlUnit offre par ailleurs un bon support javascript et ajax. A noter simplement que son utilisation passe souvent par une encapsulation dans un framework « maison » afin de faciliter l’utilisation de son API.

L’écriture des tests web prend du temps et peut demander une modification de vos pages si vous n’avez pas identifié de façon unique vos éléments du DOM ; une bonne pratique est d’ajouter des id aux éléments à tester. De toute façon cela ne fait pas de mal. Libre à vous après de déterminer jusqu’à quel point vous voulez tester vos pages car la moindre modification du code peut faire échouer vos tests. Etant donné le temps d’écriture de ces tests il est raisonnable de ne pas tout tester.

Voyons plus en détails JWebUnit et selenium :

JWebUnit

JWebUnit est aussi un framework java d’écriture de tests d’application Web ; il est par contre d’un plus haut niveau qu’HTMLUnit puisqu’il l’encapsule ! Le code de JWebUnit est plus concis qu’HTMLUnit et permet en théorie d’utiliser 2 moteurs de tests : HtmlUnit et Selenium.

Exemple de scénario de test :

  • on se connecte au site http://www.google.fr/
  • on entre dans le champ input de recherche le mot «htmlunit »
  • on clique sur recherche
  • on clique sur un lien qui contient le texte « HtmlUnit »
  • on vérifie que le titre de la page affichée est « HtmlUnit – Welcome to HtmlUnit »
  • on vérifie la présence du texte « Get started »

Avec le plugins htmlUnit jwebunit-htmlunit-plugin-2.0.jar dans votre build path :

import junit.framework.TestCase; import net.sourceforge.jwebunit.htmlunit.HtmlUnitTestingEngineImpl; import net.sourceforge.jwebunit.junit.WebTester; public class TestSearchExample extends TestCase { private WebTester tester; public TestSearchExample(String name) { super(name); tester = new WebTester(); HtmlUnitTestingEngineImpl engine = new HtmlUnitTestingEngineImpl(); tester.setDialog(engine); tester.getTestContext().setBaseUrl("http://www.google.fr/"); } public void testSearch() throws Exception { tester.beginAt("/"); tester.setTextField("q", "htmlunit"); tester.submit("btnG"); tester.clickLinkWithText("HtmlUnit"); tester.assertTitleEquals("HtmlUnit - Welcome to HtmlUnit"); tester.assertLinkPresentWithText("Get started"); } }

Si vous voulez utiliser le plugins selenium jwebunit-selenium-plugin-2.0.jar, vous devez au préalable démarrer le serveur selenium remote contrôle :

java -jar C:\cheminVers\selenium-server.jar

import junit.framework.TestCase; import net.sourceforge.jwebunit.junit.WebTester; import net.sourceforge.jwebunit.selenium.SeleniumTestingEngineImpl; public class TestSearchGoogleSelenium extends TestCase { private WebTester tester; public TestSearchGoogleSelenium(String name) { super(name); tester = new WebTester(); SeleniumTestingEngineImpl engine = new SeleniumTestingEngineImpl(); tester.setDialog(engine); tester.getTestContext().setBaseUrl("http://www.google.fr/ig?hl=fr"); } public void testSearch() throws Exception { tester.beginAt("/"); tester.setTextField("q", "htmlunit"); tester.submit("btnG"); tester.clickLinkWithText("HtmlUnit"); tester.assertTitleEquals("HtmlUnit - Welcome to HtmlUnit"); tester.assertLinkPresentWithText("Get started"); } }

Ce code est une façon de faire mais il existe une autre façon d’initialiser le moteur de test.

Derrière la scène, les implémentations des moteurs de tests Selenium et HtmlUnit ne fonctionnent pas de la même façon : SeleniumTestingEngineImpl utilise XPath.

Au lancement de ce test, un navigateur FireFox est ouvert avec la page de recherche de google et le rendu du scénario. Si tout se passe bien, ce test JUnit dans Eclipse passe au vert. Comme vous venez de le voir l’API JWebUnit est très explicite dans ses noms de méthodes.

Si le code de votre application est déjà écrit, utiliser FireBug pour obtenir les id des éléments que vous testez est une façon de faire. Une autre solution est d’utiliser selenium IDE.

A signaler qu’après avoir fait quelques tests avec HtmlUnit 2.3 il semble que la méthode Ajax.Updater ne passe pas :=(

**Selenium****

**

Signalons tout d’abord qu’une nouvelle version du site de selenium est sortie : http://selenium.openqa.org/

Qu’est ce que Selenium ? Une suite d’outils de la communauté OpenQA permettant de tester vos applications Web dans de vrais navigateurs. Vos tests seront donc plus fidèles aux comportements des utilisateurs qu’avec des émulations de navigateurs.

Selenium IDE c’est

  • grande simplicité d’utilisation,
  • multi-langage,
  • multi-platforme,
  • parallélisation des tests

Selenium IDE

Il s’agit d’un plugin de FireFox qui permet d’enregistrer un scénario de test de votre application. Les clics, saisies de données, vérification de présence de texte sont enregistrées sous forme d’un script pour être rejouées plus tard sous selenium IDE ou exportées dans votre langage de choix : java, Ruby, PHP, perl, C#, ou Python.

Sous FireFox :

  • allez sur la page à tester,
  • cliquez sur Outils,
  • sélectionnez selenium IDE.

Une fenêtre s’ouvre en mode « recording » enregistrez votre scénario.

Pour un teste du titre de la page : sélectionner le libellé, cliquez droit et choisissez la commande « assertTextPresent ». Pour un simple test de libellé utilisez la commande « verifyTextPresent ». La différence entre les deux commandes est que « verifyTextPresent », si elle échoue, ne bloque pas la suite du test. A la fin de votre scénario cliquez sur le bouton rouge pour arrêter l’enregistrement.

Pour sauvegarder le script et pouvoir le rejouer cliquez sur « Fichier », « sauver le test sous ». Pour l’exporter dans un format plus évolué qu’un script exemple au format java, sélectionnez « exporter le test sous »

Vous aurez des fois par ailleurs besoin de redefinir la base URL.

Un exemple en java :

package springapp.domain; import com.thoughtworks.selenium.*; public class TestSpringApp1 extends SeleneseTestCase { public void setUp() throws Exception { setUp("http://localhost:8060/springapp/hello.htm", "*chrome"); } public void testNew() throws Exception { selenium.open("/springapp/hello.htm"); assertTrue(selenium.isTextPresent("Increase Prices")); selenium.click("link=Increase Prices"); selenium.waitForPageToLoad("30000"); selenium.type("percentage", "30"); selenium.click("//input[@value='Execute']"); selenium.waitForPageToLoad("30000"); verifyTrue(selenium.isTextPresent("Products")); } }

Ce test java va pouvoir être exécuté grâce à seleniumRC.

Avant de lancer ce test depuis Eclipse il est nécessaire de lancer le serveur seleniumRC :

java -jar C:\java\selenium-remote-control-0.9.2-dist\selenium-remote-control-0.9.2\selenium-server-0.9.2\selenium-server.jar

Maintenant vous pouvez le lancer ; une fenêtre « selenium Functional Testing for Web Apps » apparaît avec le déroulement de votre scénario dans la partie basse de votre navigateur.

Si dans votre code vous avez dans un onload un window.open, il semble que selenium n’intercepte pas l’ouverte de cette nouvelle fenêtre (sel-339). Un moyen de contournement est de lancer vous même l’ouverture de votre nouvelle fenêtre sans laisser cette charge à la fenêtre qui contient l’onload par un : selenium.openWindow(“URLInOnload”, “nameOfWindow”) soit un selectWindow | | nameOfWindow dans l’IDE de selenium, puis selenium.selectWindow(“nameOfWindow”). Le focus est désormais sur la nouvelle fenêtre.

Quelques conseils pour la saisie de votre script :

  • utilisez comme première commande un assertTextPresent d’un libellé qui garantit que la page est bien chargé puis une série de verifyTextPresent
  • si lors d’un click sur un lien selenium IDE génère un click et que le chargement prend alors un peu de temps utilisez un clickAndWait pour attendre que la page suivante soit bien chargée et éviter ainsi que le test selenium n’échoue parce qu’il serait déjà passé à la commande suivante. Vous pouvez aussi utiliser un click puis un waitForTextPresent.
  • il peut arriver aussi que l’IDE lors de la première ouverte ne soit pas opérationnel. Réouvrez le simplement et vous pourrez saisir votre test.

SeleniumRC 

SeleniumRC est un outil de test qui permet de lancer des tests graphiques d’application web dans plusieurs langages de programmation pour des sites http sous différents navigateurs (IE, FF, safari) et systèmes d’exploitation (Windows, Linux, Mac). Vous pouvez donc tester toutes ces combinaisons dans des conditions réelles d’utilisation. Ce qui est quand même un gros plus !

Nous venons de voir comment lancer un test de cas d’utilisation. Mais comment en lancer plusieurs et les regrouper ? La solution vient par la création de testsuites.

Sous Selenium IDE faites

  • new test suit
  • déroulez votre premier scénario
  • sauvez le test sous (au format html)
  • save Test suit as (au format html)

Pour rajouter un deuxième test dans votre test suit faites :

  • Nouveau Test (soit depuis le menu soit depuis la partie gauche qui liste les tests)
  • Déroulez votre deuxième scénario
  • Sauvez le test sous (au format html)
  • Save Test suit (au format html)

Si vous fermez Selenium IDE et que vous faites Open Test suit vous verrez vos deux tests.

Voici par exemple un exemple contenant 3 tests :

Test Suite
Test Suite
testCase1
testCase2
testCase3

Sous Selenium IDE il est possible de lancer ces tests en cliquant sur le bouton « lancer avec selenium TestRunner » ou bien sur le bouton « play entire test suit »

Parallélisation des tests avec Selenium Grid**


Si vous êtes en intégration continue dans votre projet, vous aurez peut être envie que chaque batterie de tests ne prennent pas une heure. Vous pourrez avoir plusieurs « selenium remote control » sur des machines différentes qui offriront des configurations différentes de navigateurs et de systèmes d’exploitations. Mais que peut-on faire si la ou les machines qui exécutent les tests n’ont pas de navigateurs ?

Il est possible d’utiliser un plugins VNC (exemple : http://hudson.gotdns.com/wiki//display/HUDSON/Xvnc+Plugin ) ou Xvfb qui est un simulateur de server x sous linux.

Fonctionnement de Selenium grid : http://selenium-grid.openqa.org/how_it_works.html

Selenium Grid permet donc de lancer des instances différentes de « selenium remote control » en parallèle. Selon ce site, 5 machines puissantes abritant chacune « 10 remote control » suffisent à diviser le temps des tests fonctionnels par 50 ! Soit une progression linéaire.  Si vous voulez voir ce que cela donne : http://selenium-grid.openqa.org/run_the_demo.html

Quelques liens intéressants :