Blog de David Masclet - Mot-clé - JsMock2020-02-11T15:05:04+00:00urn:md5:bd7c47a89c3b735a7167e4bd4cde9285DotclearTester le javascript : JSUnit, JSmock, Jstester (partie 2)urn:md5:b9770f550fd0917bd06317a887a109ef2010-01-26T13:16:00+01:002015-06-07T11:24:15+02:00MD3804-GANDITests / XUnitJavascriptJsMockJsUnittest<p>Après le <a href="https://davidmasclet.gisgraphy.com/post/2010/01/08/Tester-le-javascript-%3A-JSUnit%2C-JSmock%2C-Jstester-%28partie-1%29">
billet</a> consacré à JsUnit et <a href="https://davidmasclet.gisgraphy.com/post/2009/03/13/Mock-ajax-request">celui</a> qui expliquait comment mocker
une requête Ajax, nous allons maintenant voir comment mocker en javascript.
Pour cela je vous propose une courte présentation de JsMock. Les exemples de
code sont en annexe de ce billet.</p> <p>Pour montrer les capacités de JsMock, rien de tel que des exemples de code.
Nous allons donc reprendre le code du <a href="https://davidmasclet.gisgraphy.com/post/2010/01/08/Tester-le-javascript-%3A-JSUnit%2C-JSmock%2C-Jstester-%28partie-1%29">
premier billet</a> (vous pouvez télécharger les sources en annexe du premier
billet)</p>
<h3>Installation</h3>
<p>La première chose à faire est de télécharger <a href="http://jsmock.sourceforge.net/" hreflang="en">JsMock</a> et de l'ajouter dans
le répertoire JsUnit (ou ailleurs, mais le tout est d'être cohérent lors de la
déclaration dans le fichier HTML) , puis de l'importer à partir de notre
fichier de tests <code>SimpleCodeTest</code> :</p>
<pre>
<script language="JavaScript" type="text/javascript" src="../jsmock.js"></script>
</pre>
<p><br /></p>
<h3>Exemple de code à mocker</h3>
<p>Pour comprendre les mocks il vaux mieux faire interagir deux objets entre
eux : cela permet de tester un objet et d'en mocker un autre. Ajoutons
donc un objet <code>ConfigParameter</code> et définissons en une instance dans
MyObjectToTest :</p>
<pre>
function ConfigParameter() {
this.getAlertMessage = function(){
return "alertMessage";
}
this.setParam = function(Aname, Avalue) {
//this[name]=value
}
}
function MyObjectToTest(){
this.configParameter = new ConfigParameter();
var message = "Not defined yet!";
this.setMessage = function(messageParam){
message = messageParam
}
this.getMessage = function(){
return message;
}
this.getAlertMessage = function(){
return this.configParameter.getAlertMessage();
}
this.setConfigValue = function(){
this.configParameter.setParam("MyName", "MyValue");
}
}
</pre>
<p><br /></p>
<h3>Mocker des valeurs de retour</h3>
<p>Voici un exemple où nous allons mocker le retour de
<code>getAlertMessage</code> :</p>
<pre>
function testMockReturnedValue() {
// Actor
MyObj = new MyObjectToTest();
// Mock
mockControl = new MockControl();
mockConfig = mockControl.createMock(ConfigParameter);
mockConfig.expects().getAlertMessage().andReturn("mock Alert Message");
MyObj.configParameter = mockConfig;
// Assert
assertEquals("mock Alert Message", MyObj.getAlertMessage());
mockControl.verify();
}
</pre>
<p>Vous remarquerez qu'à la différence de EasyMock en Java, il n'est pas
nécessaire d'appeler la méthode <code>replay()</code></p>
<p>La méthode <code>vérify</code> permet de vérifier que les méthodes mockées
sont bien appelées le bon nombre de fois : si une méthode non défini par
votre mock est appelée le test ne passera pas, en revanche vous pourriez avoir
défini le comportement d'une méthode mais que cette dernière ne soit jamais
appelée, dans ce cas, si vous n'appelez pas la méthode <code>verify</code>, le
test passera mais vous ne saurez pas que la méthode n'a pas été appelée).</p>
<p><br /></p>
<h3>mocker des fonctions</h3>
<p>Il est possible de simuler les comportement (le corps de la méthode) d'une
fonction grâce à la méthode <code>andStub</code> :</p>
<pre>
function testStubFunction() {
// Actor
MyObj = new MyObjectToTest();
// Mock
mockControl = new MockControl();
mockConfig = mockControl.createMock(ConfigParameter);
mockConfig.expects().getAlertMessage().andStub(
function() {
return "test";
}
);
MyObj.configParameter = mockConfig;
// Assert
assertEquals("test", MyObj.getAlertMessage());
}
</pre>
<p><br /></p>
<h3>Simuler des exceptions</h3>
<p>Il est également possible de simuler des exceptions avec la méthode
<code>andThrow</code> :</p>
<pre>
function testThrow() {
exception = false ;
// Actor
MyObj = new MyObjectToTest();
// Mock
mockControl = new MockControl();
mockConfig = mockControl.createMock(ConfigParameter);
mockConfig.expects().getAlertMessage().andThrow("Error !");
MyObj.configParameter = mockConfig;
try {
MyObj.getAlertMessage();
} catch (e){
exception = true;
}
assertEquals(true, exception);
}
</pre>
<p><br /></p>
<h3>Mocker des fonctions avec paramètres</h3>
<p>Pour mocker une fonction avec des paramètres, vous avez deux
possibilités : soit vous spécifiez le type des paramètres lors de la
définition du mock, dans le cas où seul le type des paramètres vous importe.
Pour cela utiliser <code>TypeOf.isA()</code></p>
<pre>
function testFunctionWithTypedParameters() {
// Actor
MyObj = new MyObjectToTest();
// Mock
mockControl = new MockControl();
mockConfig = mockControl.createMock(ConfigParameter);
mockConfig.expects().setParam(TypeOf.isA(String),TypeOf.isA(String));
MyObj.configParameter = mockConfig;
MyObj.setConfigValue();
mockControl.verify();
}
</pre>
<p>soit vous spécifiez la valeur des paramètres, si vous voulez vérifier que la
fonction est appelée avec les bonnes valeurs :</p>
<pre>
function testFunctionWithParameters() {
// Actor
MyObj = new MyObjectToTest();
// Mock
mockControl = new MockControl();
mockConfig = mockControl.createMock(ConfigParameter);
mockConfig.expects().setParam("MyName", "MyValue");
MyObj.configParameter = mockConfig;
MyObj.setConfigValue();
mockControl.verify();
}
</pre>
<p>J'espère que cela vous à donner envie de l'utiliser, car le Javascript est
de plus en plus présent dans les applications web et les tests permettent de
mieux découpler votre code et de le rendre plus robuste.</p>
<p>La troisième et dernière partie traitera de <code>JsTester</code> qui permet
de tester du javascript coté serveur (JsUnit dispose d'une partie serveur mais
elle n'est là que pour piloter les tests, il faut toujours avoir un
navigateur). nous verrons ses avantages et ses inconvénients et comment
l'intégrer avec Maven et JUnit.</p>Tester le javascript : JSUnit, JSmock, Jstester (partie 1)urn:md5:ee12c3e8fbdea5f9600419051b76f2de2010-01-08T10:59:00+01:002015-06-07T11:24:27+02:00MD3804-GANDITests / XUnitjavascriptJsMockJsTesterJsUnittests<p>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 <a href="https://davidmasclet.gisgraphy.com/post/2009/03/13/Mock-ajax-request">comment mocker une requête Ajax</a>,il y a
quelques mois) et représente une part non négligeable de code.</p>
<p>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 :</p>
<ul>
<li>JsUnit : Qui fournit des mécanismes d'assertions et d'exécutions de
tests.</li>
<li>JsMock : Permet de simuler des comportements pour des objets
Javascript,tout comme EasyMock le fait</li>
<li>JsTester : Permet d'executer du code Javascript en java grâce à
<a href="http://www.mozilla.org/rhino/" hreflang="en">Rhino</a>.</li>
</ul>
<p>les exemples de code sont disponibles en annexe de ce billet</p> <p>Commençons par JsUnit.</p>
<h4>Installation</h4>
<p>Vous pouvez le télécharger sur le site de <a href="http://www.jsunit.net/" hreflang="en">JsUnit</a>. 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 :</p>
<p><img src="https://davidmasclet.gisgraphy.com/public/.jsunit_m.jpg" alt="jsunit" style="display:block; margin:0 auto;" title="jsunit, janv. 2010" /></p>
<p>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.</p>
<p>Si vous ne désirez pas installer JsUnit vous pouvez <a href="http://www.jsunit.net/runner/testRunner.html?testpage=/runner/tests/jsUnitTestSuite.html">
exécuter le testRunner sur le site de jsUnit</a></p>
<h4>Utilisation</h4>
<p>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:</p>
<pre>
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
*/
</pre>
<p>Créons un répertoire "testMe" dans le dossier de JsUnit et sauvegardons le
contenu dans un fichier "simpleCode.js"</p>
<p>Ecrivons ensuite un fichier "SimpleCodeTest.html", contenant plusieurs cas
de tests</p>
<pre>
<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>
</pre>
<p>Comme vous pouvez le constater JsUnit dispose de plusieurs points communs
avec JUnit :</p>
<ul>
<li>Les tests doivent commencer par test, suivi d'une majuscule, afin que la
méthode soit détectée.</li>
<li>Des mécanismes permettant de faire des assertions sont fournis.</li>
<li>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.</li>
</ul>
<p>Des assertion spécifiques au Javascript sont également disponibles :
assertUndefined, assertNotUndefined, assertNaN,...</p>
<p>Déroulons maintenant notre test :</p>
<ul>
<li>Ouvrons la page testRunner.html dans un navigateur</li>
<li>Sélectionnons le fichier SimpleCodeTest.html</li>
<li>Cliquons sur "Run"</li>
</ul>
<p>Vous verrez alors : Runs: 3 Errors: 0 Failures: 0 et la fameuse "barre
verte" prouvant que votre code est conforme aux tests.</p>
<p>Voyons maintenant avec un test qui échoue. Ajoutons ce cas de
test :</p>
<pre>
function testThatFail() {
// Actor
MyObj = new MyObjectToTest();
// Action
MyObj.setMessage("this is not it");
// Assert
assertEquals("This is it", MyObj.getMessage());
}
</pre>
<p>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.</p>
<h4>Problèmes courants et solutions</h4>
<ul>
<li>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 :</li>
</ul>
<pre>
http://path/to/jsunit//testRunner.html?testpage=/path/to/jsunit/testMe/simpleCodeTest.html
</pre>
<ul>
<li>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.</li>
</ul>
<h4>Utilisation avancée</h4>
<ul>
<li>Vous pouvez définir un suite de tests (aka : testSuite) grâce aux
méthodes <code>addTestPage(filename)</code> et
<code>addTestSuite(aTestSuite)</code></li>
</ul>
<ul>
<li>Des fonction de logging sont fournies :</li>
</ul>
<pre>
function warn(message, [value])
function inform(message, [value]) (equivalent to function info(message, [value]))
function debug(message, [value])
</pre>
<ul>
<li>Vous pouvez définir des timeouts d'exécution des tests sur la page du
testRunner.html</li>
</ul>
<ul>
<li>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.). <a href="http://www.jsunit.net/documentation/customQueryStrings.html" hreflang="en">Plus d'infos</a></li>
</ul>
<ul>
<li>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 <a href="http://www.jsunit.net/documentation/serverOverview.html" hreflang="en">version
serveur</a> est fournies (personnellement, je la trouve lourde
d'utilisation)</li>
</ul>
<p>Lors du prochain post, nous verrons comment mocker les objets javascript et
simuler le DOM HTML</p>