Yoan Blanc’s weblog

Another lost swiss guy

Writing testable JavaScript

Yoan BlancSat, 06 Feb 2010, ,

When some people still focus on documentation, I tend to slip on saying that it’s not that important.

“Working software over comprehensive documentation”

Manifesto for Agile Software Development

Most of us already know about unobtrusive JavaScript and how good it is. Maybe also about event delegation and how many listeners you are sparing with that little technique (thanks to jQuery’s closest). I’ll be a bit more generalist here and talking about writing code that enable you to test them with unit tests (using QUnit, YUI, …).

Before you think about it, yes Test Driven Development or Behavioural Driven Development (which are very close if you do the first one right) will force to you do so. If you know those, go with them but the following pitfalls might still be interesting.

Don’t use the module pattern when you really mean class.

var Foo = {
 init: function(state) {
  this.state = state;
 }
}

Foo.init(42);

How many instances of Foo can you have on your page once initialized ? The answer is one as this represent a kind of singleton. It also means that you won’t be able to test as many instances as you want representing tons of different cases. Stick with the good ol’ way.

function Foo(state) {
 this.state = state;
}

new Foo(42);

Stay away from id’s as well for the same reason and try to use classes instead. It means no document.getElementById within Foo’s constructor.

Separate the content from the logic, you JavaScript module/class should be about the logic only. Strings, translations, configuration, ... should be kept outside. Giving a JSON structure as only parameter of a constructor is common and readable.

new Foo({
 class: "foo",
 quantity: 42
});

jQuery’s extend (YUI’s ..., Prototype ...) offer a good way to mix good default values with configuration.

Next one is loose coupling. Being sure that a module what work as well alone or when it has to interact with many other ones is key when building big and complexe systems.

The involved technique is to rely on an event mechanism. On a very safe web environment (like Opera widgets, Pre application, Apple dashboard, …), DOM3 events can be used. You only have be tied to a DOM node to fire them. YUI implements an easy to use Observer pattern (called CustomEvent in YUI2), jQuery offers something similar which integrates with how it manages DOM events (Prototype as well).

// b will send data to a
var a = new A();
var b = new B(a);

// or
var a = new A();
var b = new B();
a.onChange(b.onChanged, b);

Those are very similar, thus it seems easier to adapt, test or reuse. Isn’t it?terminal

And last but not least, don’t put your init in the libraries/modules/… Anything that involves onload, DOMContentReady, … is tied to a particular usage already and shouldn’t live into a library or module imho.

function A(element) {
 // whatever
}

$(document).ready(function() {
	new A($("#a")[0]);
});

It means that those two blocks of code should live separately. At last in your developement environment. Then, you’re totally free to join, compress and maybe obfuscate them as you won’t test or develop against that version.

And it’s more a personal taste issue here. I don’t like stuff that are automagically loaded because of a particular class name or id.

If you see more of them, feel free to contribute.

Si le must reste de pratiquer le développement piloté par les tests (TDD) ou « comportemental » (Behavioural), il me semble judicieux de pointer différents points qui vont être des freins à la mise en place de tests. En somme des mauvaises pratiques où la justification est qu’elles rendent problématique la mise en place d’une assurance qualité.

  • Mon préféré est le module pattern, qui cache un singleton, donc état global ce qui implique des difficultés à une éventuelle réutilisation.
  • Mélanger l’instanciation et la définition. Il est plus aisé d’effectuer des tests sur une portion ne faisant rien par défaut, ne s’initialisant magiquement dès qu’elle le peu. Séparer la définition de l’usage est important.
  • Fournir de bons défauts mais offrir des possibilités de modification. Un but premier est la réutilisabilité et, chose souvent ignorée, l’interopérabilité entre différents modules, bibliothèques. Et dans une certaine mesure le fait qu’il faille peut-être offrir une version dans la langue de l’utilisateur.
  • Pas d’id en dur, uniquement des classes. Pour la simple et bonne raison qu’une id est unique et sera toujours indépendante de votre module/bibliothèque.
  • Couplage faible entre modules, voire aucun couplage. Si deux éléments doivent communiquer pensez à leur offrir un canal approprié et ne leur donnez pas la possibilité de le faire directement. Il existe pléthore de solutions via DOM3 ou la bibliothèque de votre choix.
  • Et peut-être le plus évident, écrivez des tests. Car la réalité vaudra tous les discours du monde.

Il n’est jamais simple de franchir le pas, de s’y mettre, d’oser les tests. C’est un investissement valable. N’hésitez pas à agrémenter la liste ci-dessus de vos expériences personnelles.

About

meYoan Blanc is a web developer that lives in Switzerland (Jaquet-Droz 6, 2300 La Chaux-de-Fonds) and works as a freelance.

Get my vCard or contact me by phone (skype:yoan.blanc) or email ().

Misc

RSS, list.blogug.ch

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

copyright 2006-2009 — doSimple.ch