Tester le javascript : JSUnit, JSmock, Jstester (partie 1)

Beaucoup de développeurs Java sont familiers avec Junit, EasyMock,... ces librairies qui permettent de tester du code Java afin de rendre les programmes plus robustes. Javascript dispose également de frameworks équivalents. Avec l'apparition du Web 2.0, le Javascript et l'Ajax sont de plus en plus présents dans nos applications (j'avais d'ailleurs poster un billet sur comment mocker une requête Ajax,il y a quelques mois) et représente une part non négligeable de code.

Si vous utilisez GWT, qui permet d'écrire à partir de code écrit en Java, de générer une application en HTML / javascript, vous n'avez pas de problème pour tester votre code, puisque Junit suffit. En revanche, si vous avez du code javascript à tester, il existe des librairies qui permettent de simplifier les tests. Je vous propose de voir comment tester du Javascript au moyen de ces 3 frameworks :

  • JsUnit : Qui fournit des mécanismes d'assertions et d'exécutions de tests.
  • JsMock : Permet de simuler des comportements pour des objets Javascript,tout comme EasyMock le fait
  • JsTester : Permet d'executer du code Javascript en java grâce à Rhino.

les exemples de code sont disponibles en annexe de ce billet

Commençons par JsUnit.

Installation

Vous pouvez le télécharger sur le site de JsUnit. Une fois télécharger décompresser le. JsUnit dispose d'une partie serveur que nous ne verrons pas ici (peut être dans un autre billet) et d'une partie cliente. Pour lancer la partie cliente, il faut ouvrir la page testRunner.html,qui se trouve à la racine du dossier que vous avez décompressé, dans un navigateur :

jsunit

Cette page vous permet de lancer un fichier contenant des tests écrits en Javascript et de vous afficher le résultat de l'exécution.

Si vous ne désirez pas installer JsUnit vous pouvez exécuter le testRunner sur le site de jsUnit

Utilisation

Je vous propose de tester un code très simple avec un objet MyObjectToTest disposant d'une variable privée, d'un getter et d'un setter:

function MyObjectToTest(){
        var message = "Not defined yet!";
        
        this.setMessage = function(messageParam){
                message = messageParam
        }

        this.getMessage = function(){
                return message;
        }

}       
/*example of use
MyObj = new MyObjectToTest();
alert(MyObj.getMessage());//Not defined yet!
MyObj.setMessage("This is it");
alert(MyObj.getMessage() );//This is it
*/

Créons un répertoire "testMe" dans le dossier de JsUnit et sauvegardons le contenu dans un fichier "simpleCode.js"

Ecrivons ensuite un fichier "SimpleCodeTest.html", contenant plusieurs cas de tests

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>test de SimpleCode.html</title>

</head>

<body>
<script language="JavaScript" type="text/javascript" src="../app/jsUnitCore.js"></script>
<script language="JavaScript" type="text/javascript" src="./simpleCode.js"></script>
<script language="JavaScript" type="text/javascript">
function setUp(){
        //nothing to do
}
function tearDown(){
        //nothing special
}

function testMessageShouldBePrivate() {
        // Actor
        MyObj = new MyObjectToTest();
        
        // Action
        
        // Assert
        assertEquals("undefined", typeof MyObj.message);
        assertUndefined(MyObj.message);
}

function testMessageShouldHaveADefaultValue() {
        // Actor
        MyObj = new MyObjectToTest();
        
        // Action
        
        // Assert
        assertEquals("Not defined yet!", MyObj.getMessage());
}


function testGetAndSetMessageShouldGetAndSet() {
        // Actor
        MyObj = new MyObjectToTest();
        
        // Action
        MyObj.setMessage("This is it");                       

        // Assert
        assertEquals("This is it", MyObj.getMessage());
}

</script>
</body>
</html>

Comme vous pouvez le constater JsUnit dispose de plusieurs points communs avec JUnit :

  • Les tests doivent commencer par test, suivi d'une majuscule, afin que la méthode soit détectée.
  • Des mécanismes permettant de faire des assertions sont fournis.
  • Il y a la possibilité de définir des méthodes qui seront appelés avant ( setUp() ) et après chaque test ( tearDown() ). Noter que le nom et la casse ont une importance.

Des assertion spécifiques au Javascript sont également disponibles : assertUndefined, assertNotUndefined, assertNaN,...

Déroulons maintenant notre test :

  • Ouvrons la page testRunner.html dans un navigateur
  • Sélectionnons le fichier SimpleCodeTest.html
  • Cliquons sur "Run"

Vous verrez alors : Runs: 3 Errors: 0 Failures: 0 et la fameuse "barre verte" prouvant que votre code est conforme aux tests.

Voyons maintenant avec un test qui échoue. Ajoutons ce cas de test :

function testThatFail() {
        // Actor
        MyObj = new MyObjectToTest();
        
        // Action
        MyObj.setMessage("this is not it");                   

        // Assert
        assertEquals("This is it", MyObj.getMessage());
}

En re-exécutant les tests vous aurez : Runs: 4 Errors: 0 Failures: 1 , une "barre rouge", et "testThatFail" dans la liste des tests échoués. en cliquant dessus, vous aurez la stacktrace d'exécution.

Problèmes courants et solutions

  • Pour des raisons que j'ignore il peux arriver qu'en local, le fichier de test ne soit pas trouvé, il faut alors appeler l'URL de testRunner.html avec le paramètre testpage :
http://path/to/jsunit//testRunner.html?testpage=/path/to/jsunit/testMe/simpleCodeTest.html
  • Sur Firefox, si la version est supérieure ou égale à 3.0 des soucis dus à la politique de sécurité peuvent subvenir. il faut alors taper about:config dans la barre d'adresse et mettre la valeur de security.fileuri.strict_origin_policy a false en cliquant dessus. Redémarrer FireFox, et recommencer le test.

Utilisation avancée

  • Vous pouvez définir un suite de tests (aka : testSuite) grâce aux méthodes addTestPage(filename) et addTestSuite(aTestSuite)
  • Des fonction de logging sont fournies :

function warn(message, [value])
function inform(message, [value]) (equivalent to function info(message, [value]))
function debug(message, [value])

  • Vous pouvez définir des timeouts d'exécution des tests sur la page du testRunner.html
  • Vous pouvez définir vos propres paramètres pour l'exécution de vos tests (e.g : myTests.html?mode=0, ou myTests.html?mode=1.). Plus d'infos
  • Si vous voulez exécuter vos tests à partir d'un outil d'intégration continue ou avec des outils comme maven, ant, ou buildr, une version serveur est fournies (personnellement, je la trouve lourde d'utilisation)

Lors du prochain post, nous verrons comment mocker les objets javascript et simuler le DOM HTML

La discussion continue ailleurs

URL de rétrolien : https://davidmasclet.gisgraphy.com/index.php?trackback/44

Fil des commentaires de ce billet