Yoan Blanc’s weblog

Another swiss guy lost in Paris

Test-Driven Development with JavaScript

Yoan BlancSun, 24 Jun 2007, , ,

This Friday we were speaking about TDD a the work. As Web Developers our interest in in JavaScript tools for this purpose. Personnally, I did some with PHP stuff but not with JavaScript until today. Talking with Mr jQuery, John Resig, probably makes me choosing jQuery for this. As far as I know YUI for example doesn’t have any unit testing tools, but they are working on it. When you write code for lots of people, about myself it’s a few but for a company it can be millions. Having test for your code is one tool to guarantee quality for now, tomorrow and a far future. Unit test can, sometimes, be used in parallels with the documentation if it exists.

Let’s start. Download the empty files (one HTML, one CSS and two JS (jQuery and testrunner)) and start writing some tests.

test("myObj", function() {
 ok(myObj !== undefined, "myObj is undefined");
 ok(typeof myObj == "object", "myObj isn’t an object");
});

This says, I want to have an object called myObj. Now run the test and it will obviously fail. ok is like assertTrue, it test if the first expression is true. The second parameter is an optional label. If you’re familiar with TDD I’ll not repeat that this step is crucial. Write the test before writing the code, express what you want before saying how you’ll doing it. Say what you want not how you’ll do it the only advise I remember from my teacher of Software Engineering for the Analysis phase.

So after running this test, it’s really important too. It fails. Let’s fix it.

var myObj = (function(){
 return {};
})();

This syntax is one of the way to do OOP in JavaScript, use the one you like the more. Now re-run the tests, it should work. If it actually is, write more tests. For an example, the same old and boring example will be used, sorry for that.

test("myObj.add()", function() {
 expect(2);

 ok(typeof(myObj.add) == "function");
 equals(myObj.add(1,2), 3, "1 + 2 != 3");
});

We want a function called add that will do some addition. Here to new things: expect that says explicitly how many tests we have and equals which will compare the first and the second parameters. Run, the test, it fails, great. Sometimes you can do mistake during the writing of the tests, so this why this step is important. Let’s fix this red state to a green state that makes everyone happy again.

var myObj = (function(){
 return {
  add: function(a, b) {
   return (a + b);
  }
 }
})();

Run the test suite, if it’s green you’ve made it. Let’s do something more tricky now. In this html page, there is a place called main. The aim of this is to put HTML to manipulate in it. Often a JavaScript has the goal to add some behaviour or enrich the HTML. One of your test could be putting a sample HTML, running your JavaScript and afterwards checking that it worked. I’ll do it a slightly different way by putting test in to the HTML, maybe a bad idea.

<div id="test-addAnchors">
 <p class="test">
  Hello world! http://www.example.org this is an url
 </p>
 <p class="expected">
  Hello world! <a href="http://www.example.org">www.example.org</a> this is an url
 </p>
 <p class="test">
 Hello world! http://www.example.org/?foo this is an url
 </p>
 <p class="expected">
  Hello world! <a href="http://www.example.org/?foo">www.example.org/</a> this is an url
 </p>
</div>

The test class is the text before the action was called the expected the second one. This could be achieved via an array too.

test("myObj.addAnchors()", function() {
 ok(typeof(myObj.addAnchors) == "function");

 $("#test-addAnchors > .test").each(function(){
  $(this).html(myObj.addAnchors($(this).html()));
  var expected = $(this).next().get(0);

  equals(this.innerHTML, expected.innerHTML);
 });
});

It’s a function, allright. Next step, we take all the .test apply a function called addAnchors and see if it has the same content than the next sibling (here expected). Great, it fails. Let’s fix it.

addAnchors: function(text) {
 return text.replace(/(http:\/\/)([^ ?]+)(\?[^ ]+)?/g, ’<a href="$1$2$3">$2</a>’);
}

Hop a nice RegExp. Because regexp are unreadable, my advise is to write it by yourself, and try to fix the first test then the second one. This example is very quick and poor, but jQuery is full of test suites. Read them, read the testrunner.js as well and start from this to do test as often as you can: before adding a new feature, after fixing a bug (to be sure this won’t happen again). Find the final tests here.

Happy testing.

Vendredi passé lors du repas de midi entre collègues web dev nous parlions tests unitaires et JavaScript autour de délicieux sushis. L’idée est séduisante, j’en suis convaincu mais n’en ai jamais fait pour JavaScript, un peu en PHP uniquement. Chose corrigée. Peut-être avoir pu discuter de cela avec John Resig, Monsieur jQuery qui ne s’imagine plus écrire du code allant être utilisé à grand échelle sans un minimum de tests a aidé à me faire franchir le pas. Personnellement je n’en suis pas à son niveau, mais pour être professionnel, un code partant en production mérite la touche qualité que sont ces tests là. Écrire les tests avant le code, le Test Driven Development est l’idée poussée à l’extrème, écrire des tests de non-régression (au fur et à mesure que des bogues sont corrigés) est déjà un bon point que peu peuvent se vanter de remplir.

Merci de suivre la partie anglophone de cet article pour un rapide introduction au Test-Driven Development avec JavaScript, ci-dessous je vais détailler la structure d’une page de tests avec le système utilisé.

Je me suis servi des outils utilisés par jQuery pour tester ma propre bibliothèques JavaScript. Les pages de test comportent deux parties : une zone JavaScript contenant les tests de la forme :

test("maFonction", function() {
 // nombre de tests
 expect(2);
 // un test
 ok(expression valide);
 // un autre test
 equals(maFonction(), valeur référence, "label");
});

Ensuite dans le corps de la page, une zone HTML qui va servir de bac à sable. Une zone pour mettre de l’HTML d’entrée allant être traîté par votre librairie puis ensuite testé avec les valeurs références. En principe JavaScript agit sur l’HTML, c’est donc le moyen de le manipuler.

Il serait intéressant de tester différents outils de tests unitaires en JavaScript. Pour le moment, je vais m’essayer à celui-ci, n’hésitez pas à tenter le développement piloté par les tests, s’il semble contraignant c’est très plaisant de finir avec un code qui fonctionne, car on commence par dire qu’il est cassé. Que du bonheur.

About

meYoan Blanc is a web developer that lives in Paris (19, rue Bichat75010France) works for Yahoo! and comes from Switzerland ( La Chaux-de-Fonds). This web site is for this weblog, a playground for random stuffs and can help keepingconnected with some friends out there.

Get my vCard or contact me by phone (+33 1 74 30 12 92) or email ().

Misc

RSS, list.blogug.ch

This site reflects only my opinion and is not affiliated with my employer.

copyright 2006-2007 — doSimple.ch