QUnit, développé par l'équipe de jQuery, est un excellent cadre pour le test unitaire de votre JavaScript. Dans ce didacticiel, je vais expliquer en quoi consiste exactement QUnit et pourquoi vous devez vous soucier de tester votre code de manière rigoureuse..
QUnit est un puissant framework de tests unitaires JavaScript qui vous aide à déboguer du code. Il a été écrit par les membres de l'équipe jQuery et constitue la suite de tests officielle de jQuery. Mais QUnit est assez général pour tester n'importe quel code JavaScript classique, et il est même capable de tester du JavaScript côté serveur via un moteur JavaScript comme Rhino ou V8..
Si vous n'êtes pas familier avec l'idée de "tests unitaires", ne vous inquiétez pas. Ce n'est pas trop difficile à comprendre:
En programmation informatique, le test unitaire est une méthode de vérification et de validation de logiciel dans laquelle un programmeur teste si des unités individuelles de code source sont aptes à être utilisées. Une unité est la plus petite partie testable d'une application. En programmation procédurale, une unité peut être une fonction ou une procédure individuelle.
Ceci est cité de Wikipedia. En termes simples, vous écrivez des tests pour chaque fonctionnalité de votre code et si tous ces tests sont réussis, vous pouvez être sûr que le code sera sans bug (la plupart du temps, cela dépend de la profondeur de vos tests)..
Si vous n'avez encore jamais écrit de tests unitaires, vous appliquez probablement directement votre code à un site Web, cliquez pendant un moment pour voir si un problème survient et essayez de le résoudre au fur et à mesure que vous en repérez un. Il y a beaucoup de problèmes avec cette méthode.
Tout d'abord, c'est très fastidieux. En fait, cliquer n'est pas une tâche facile, car vous devez vous assurer que tout est cliqué et il est très probable que vous manquiez une chose ou deux. Deuxièmement, tout ce que vous avez fait pour les tests n'est pas réutilisable, ce qui signifie qu'il est difficile de trouver des régressions. Qu'est-ce qu'une régression? Imaginez que vous avez écrit du code et que vous l'avez testé, corrigé tous les bugs trouvés et publié. Ensuite, un utilisateur envoie des commentaires sur les nouveaux bogues et demande de nouvelles fonctionnalités. Vous revenez au code, corrigez ces nouveaux bogues et ajoutez ces nouvelles fonctionnalités. Ce qui pourrait arriver ensuite, c’est que certains des vieux bugs remontent, qu’on appelle «régressions». Vous voyez, vous devez maintenant cliquer à nouveau, et il est probable que vous ne retrouviez plus ces vieux bogues; même si vous le faites, il faudra un certain temps avant de comprendre que le problème est causé par des régressions. Avec les tests unitaires, vous écrivez des tests pour rechercher les bogues et, une fois le code modifié, vous le filtrez à nouveau. Si une régression apparaît, certains tests échoueront certainement et vous pourrez les repérer facilement, sachant quelle partie du code contient le bogue. Puisque vous savez ce que vous venez de modifier, vous pouvez le réparer facilement..
Les tests unitaires présentent un autre avantage, notamment pour le développement Web: ils facilitent les tests de compatibilité entre navigateurs. Exécutez simplement vos tests sur différents navigateurs. Si un problème survient sur un navigateur, vous le corrigez et réexécutez ces tests, en vous assurant qu'il n'introduit pas de régression sur les autres navigateurs. Vous pouvez être sûr que tous les navigateurs cibles sont pris en charge, une fois qu'ils ont tous passé les tests..
J'aimerais mentionner l'un des projets de John Resig: TestSwarm. Il prend les tests unitaires JavaScript à un nouveau niveau, en le rendant distribué. C'est un site Web qui contient de nombreux tests, n'importe qui peut y aller, exécuter certains des tests, puis renvoyer le résultat au serveur. De cette manière, le code peut être testé très rapidement sur différents navigateurs et même différentes plates-formes.
Alors, comment écrivez-vous des tests unitaires avec QUnit exactement? Tout d'abord, vous devez configurer un environnement de test:
Suite de tests QUnit Suite de tests QUnit
Comme vous pouvez le constater, une version hébergée du framework QUnit est utilisée ici.
Le code qui va être testé doit être placé dans myProject.js et vos tests doivent être insérés dans myTests.js. Pour exécuter ces tests, ouvrez simplement ce fichier HTML dans un navigateur. Maintenant il est temps d'écrire des tests.
Les blocs de construction des tests unitaires sont des assertions.
Une assertion est une déclaration qui prédit le résultat renvoyé par votre code. Si la prédiction est fausse, l'assertion a échoué et vous savez que quelque chose s'est mal passé..
Pour exécuter des assertions, vous devez les mettre dans un scénario de test:
// Testons cette fonction function isEven (val) return val% 2 === 0; test ('isEven ()', function () ok (isEven (0), 'Zero est un nombre pair'); ok (isEven (2), 'So is two'); ok (isEven (-4) , "Ainsi est négatif quatre"); ok (! IsEven (1), "On n'est pas un nombre pair"); ok (! IsEven (-7), "Ni l'un ni l'autre n'est négatif sept"))
Ici, nous avons défini une fonction, isEven, qui détecte si un nombre est pair, et nous voulons tester cette fonction pour nous assurer qu'elle ne renvoie pas de mauvaises réponses..
Nous appelons d’abord test (), qui construit un scénario de test; le premier paramètre est une chaîne qui sera affichée dans le résultat, et le deuxième paramètre est une fonction de rappel qui contient nos assertions. Cette fonction de rappel sera appelée une fois que QUnit sera lancé.
Nous avons écrit cinq affirmations, toutes booléennes. Une assertion booléenne s'attend à ce que son premier paramètre soit vrai. Le deuxième paramètre est aussi un message qui sera affiché dans le résultat.
Voici ce que vous obtenez, une fois que vous exécutez le test:
Puisque toutes ces assertions ont passé avec succès, nous pouvons être assez sûrs que est même() fonctionnera comme prévu.
Voyons ce qui se passe si une assertion a échoué.
// Testons cette fonction function isEven (val) return val% 2 === 0; test ('isEven ()', function () ok (isEven (0), 'Zero est un nombre pair'); ok (isEven (2), 'So is two'); ok (isEven (-4) , "Ainsi est négatif quatre"); ok (! IsEven (1), "On n'est pas un nombre pair"); ok (! IsEven (-7), "Ni négatif sept"); // échoue ok (isEven (3), 'Trois est un nombre pair');)
Voici le résultat:
L'assertion a échoué parce que nous avons délibérément mal écrit. Cependant, dans votre propre projet, si le test échoue et que toutes les assertions sont correctes, vous savez qu'un bogue a été trouvé..
ok () n'est pas la seule assertion fournie par QUnit. Il existe d'autres types d'assertions utiles lors du test de votre projet:
L'assertion de comparaison, equals (), attend que son premier paramètre (qui est la valeur réelle) soit égal à son second paramètre (qui est la valeur attendue). Cela ressemble à ok (), mais affiche les valeurs réelles et attendues, ce qui facilite beaucoup le débogage. Comme ok (), il faut un troisième paramètre optionnel comme message à afficher.
Donc au lieu de:
test ('assertions', function () ok (1 == 1, 'on en vaut un');)
Vous devriez écrire:
test ('assertions', function () est égal à (1, 1, 'on est égal à un');)
Notez le dernier "1", qui est la valeur de comparaison.
Et si les valeurs ne sont pas égales:
test ('assertions', function () est égal à (2, 1, 'on est égal à un');)
Cela donne beaucoup plus d’informations et rend la vie beaucoup plus facile.
L'assertion de comparaison utilise "==" pour comparer ses paramètres, elle ne gère donc pas la comparaison de tableau ou d'objet:
test ('test', function () égal (, , 'échoue, ce sont des objets différents'); égal (a: 1, a: 1, 'échoue'); égal (( ], [], 'échoue, il y a différents tableaux'); est égal à ([1], [1], 'échoue');)
Afin de tester ce type d'égalité, QUnit fournit une autre affirmation en nature: assertion identique.
Une assertion identique, same (), attend les mêmes paramètres que equals (), mais il s'agit d'une assertion de comparaison récursive profonde qui fonctionne non seulement sur les types primitifs, mais également sur les tableaux et les objets. Les assertions, dans l'exemple précédent, passeront toutes si vous les changez en assertions identiques:
test ('test', function () same (, , 'réussit, les objets ont le même contenu'); same (a: 1, a: 1, 'réussit'); same ( [], [], 'passe, les tableaux ont le même contenu'); idem ([1], [1], 'passes');)
Notez que same () utilise '===' pour effectuer des comparaisons lorsque cela est possible. Cela sera donc utile pour comparer des valeurs spéciales:
test ('test', fonction () est égal à (0, faux, 'vrai'); identique (0, faux, 'faux'); égal à (nul, non défini, 'vrai'); même (nul, non défini, ' faux'); )
Mettre toutes les assertions dans un seul cas de test est une très mauvaise idée, car il est très difficile à maintenir et ne donne pas un résultat clair. Ce que vous devez faire est de les structurer, de les placer dans différents cas de test, chacun visant une fonctionnalité unique..
Vous pouvez même organiser des cas de test dans différents modules en appelant la fonction de module:
module ('Module A'); test ('un test', function () ); test ('un autre test', function () ); module ('Module B'); test ('un test', function () ); test ('un autre test', function () );
Dans les exemples précédents, toutes les assertions sont appelées de manière synchrone, ce qui signifie qu'elles s'exécutent les unes après les autres. Dans le monde réel, il existe également de nombreuses fonctions asynchrones, telles que les appels ajax ou les fonctions appelées par setTimeout () et setInterval (). Comment pouvons-nous tester ce genre de fonctions? QUnit fournit un type spécial de scénario de test appelé "test asynchrone", dédié aux tests asynchrones:
Essayons d'abord de l'écrire de manière régulière:
test ('test asynchrone', function () setTimeout (function () ok (true);, 100)
Voir? C'est comme si nous n'avions écrit aucune affirmation. En effet, l’assertion s’exécutant de manière asynchrone, au moment où elle a été appelée, le scénario de test était déjà terminé..
Voici la version correcte:
test ('test asynchrone', function () // Suspend le test en premier arrêt (); setTimeout (function () ok (true); // Après l'appel de l'assertion, // continue le test start (); , 100))
Ici, nous utilisons stop () pour suspendre le scénario de test, et après l'appel de l'assertion, nous utilisons start () pour continuer..
L'appel de stop () immédiatement après l'appel de test () est assez courant; alors QUnit fournit un raccourci: asyncTest (). Vous pouvez réécrire l'exemple précédent comme ceci:
asyncTest ('test asynchrone', function () // Le test est automatiquement mis en pause. setTimeout (function () ok (true); // Une fois l'assertion appelée, // continue le test start ();, 100 ))
Il ya une chose à surveiller: setTimeout () appellera toujours sa fonction de rappel, mais qu’il s’agisse d’une fonction personnalisée (par exemple, un appel ajax). Comment pouvez-vous être sûr que la fonction de rappel sera appelée? Et si le rappel n'est pas appelé, start () ne sera pas appelé et l'ensemble des tests unitaires sera bloqué:
Alors voici ce que vous faites:
// Une fonction personnalisée function ajax (successCallback) $ .ajax (url: 'serveur.php', success: succèsCallback); test ('test asynchrone', function () // Suspend le test et échoue si start () n'est pas appelé après un deuxième arrêt (1000); ajax (function () //… des assertions asynchrones commencent ( );))
Vous passez un délai d'attente pour stop (), qui indique à QUnit: "si start () n'est pas appelé après ce délai, vous devez échouer à ce test." Vous pouvez être sûr que les tests ne seront pas bloqués et vous serez averti en cas de problème..
Que diriez-vous de plusieurs fonctions asynchrones? Où mettez-vous le début ()? Vous le mettez dans setTimeout ():
// Une fonction personnalisée function ajax (successCallback) $ .ajax (url: 'serveur.php', success: succèsCallback); test ('test asynchrone', function () // Suspend le test stop (); ajax (function () //… assertions asynchrones) ajax (function () //… assertions asynchrones) setTimeout (function () start ();, 2000);)
Le délai d'attente doit être suffisamment long pour permettre aux deux rappels d'être appelés avant la poursuite du test. Mais que se passe-t-il si l'un des rappels n'est pas appelé? Comment pouvez-vous le savoir? C'est là qu'attend () entre:
// Une fonction personnalisée function ajax (successCallback) $ .ajax (url: 'serveur.php', success: succèsCallback); test ('test asynchrone', function () // Suspend le test stop (); // Indique à QUnit que vous attendez trois assertions à exécuter expect (3); ajax (function () ok (true);) ajax (function () ok (vrai); ok (vrai);) setTimeout (function () start ();, 2000);)
Vous transmettez un nombre à attendre () pour indiquer à QUnit que vous attendez X assertions à s'exécuter. Si l'une des assertions n'est pas appelée, le nombre ne correspondra pas et vous serez averti que quelque chose s'est mal passé..
Il existe également un raccourci pour expect (): vous passez simplement le nombre en tant que second paramètre à tester () ou asyncTest ():
// Une fonction personnalisée function ajax (successCallback) $ .ajax (url: 'serveur.php', success: succèsCallback); // Dites à QUnit que vous attendez trois assertions pour exécuter test ('test asynchrone', 3, function () // Suspendre le test stop (); ajax (function () ok (true);) ajax (function () ok (vrai); ok (vrai);) setTimeout (function () start ();, 2000);)
C'est tout ce que vous avez besoin de savoir pour vous lancer dans QUnit. Le test unitaire est une excellente méthode pour tester votre code avant de le publier. Si vous n'avez encore jamais écrit de tests unitaires, il est temps de commencer! Merci d'avoir lu!