Déc 25, 2019 Javascript

Maîtriser Javascript : qualité du code

Qu’est-ce qu’un code de qualité ? Sans doute un code qui fait ce qu’on avait prévu mais ça ne suffit pas. Le code doit être lisible, bien organisé, respecter les « bonnes pratiques », tant qu’à faire utiliser les possibilité les plus efficaces. Il doit aussi être performant. Enfin tout un tas de considérations que je vous propose d’explorer dans cet article.

Savoir nommer

Il n’est pas si facile que ça de nommer des variables, des fonctions, des objets, des classes. le nom doit donner une idée précise. Si vous nommez ainsi une variable :

let a = 27

Vous ne savez pas du tout à quoi va servir cette variable. Soyez plus explicite :

let age = 27

On conseille aussi d’utiliser du camelCase pour les fonction, les objets et les instances :

const maVoiture = {};
function voirMaVoiture() {}

Par contre il vaut mieux utiliser du PascalCase pour les constructeurs et les classes comme on l’a vu dans le précédent article :

class Personne {
  constructor() { ... }
}

Il était classique de sauvegarder une référence à this pour une utilisation dans une fonction :

function maFonction () {
  const self = this
  return function () { // là je peux utiliser self }
}

On a vu qu’on dispose maintenant des fonctions fléchées qui résolvent ce problème :

function maFonction () { 
  return () => { // là je peux utiliser this } 
}

Enfin vous verrez souvent le nom des constantes en capitales :

const LIMITE = 100

C’était une saine pratique avant l’arrivée de const. Le débat est désormais ouvert et semble s’orienter sur le fait de capitaliser uniquement une constante exportée :

export const RULE = '212'

Les linters

Un linter a pour but d’améliorer le code. Il détecte les erreurs, le soucis de syntaxe, le non respect des bonnes pratiques… Il en existe plusieurs pour Javascript mais les deux principaux sont JSLint et ESLint, avec généralement une préférence marquée pour le second.

ESLint est donc un outil pour repérer les problèmes dans notre code. La plupart des IDE proposent des plugins pour l’installer mais on peut aussi l’installer dans un projet. ESLint est totalement paramétrable pour l’adapter à nos besoins.

Voyons comment l’installer dans un projet :

npm init -y

npm i eslint --save-dev

npx eslint --init
? How would you like to use ESLint? To check syntax, find problems, and enforce code style
? What type of modules does your project use? JavaScript modules (import/export)
? Which framework does your project use? None of these
? Does your project use TypeScript? No
? Where does your code run? Browser
? How would you like to define a style for your project? Use a popular style guide
? Which style guide do you want to follow? Airbnb: https://github.com/airbnb/javascript
? What format do you want your config file to be in? JavaScript
Checking peerDependencies of eslint-config-airbnb-base@latest
The config that you've selected requires the following dependencies:
eslint-config-airbnb-base@latest eslint@^5.16.0 || ^6.1.0 eslint-plugin-import@^2.18.2
? Would you like to install them now with npm? Yes

J’ai mis en place un fichier de configuration en répondant à quelques questions. En particulier j’ai choisi le guide de style d’Airbnb, c’est actuellement un des plus populaires.

On va maintenant soumettre un fichier Javascript. pour l’exemple je vais copier un ancien script :

var actu = new Date();
var annee = actu.getFullYear();
var anni = new Date("27 March, " + annee);
var intervalle = anni.getTime() - actu.getTime();
intervalle = Math.floor(intervalle / (1000 * 60 * 60 * 24));
document.write("Aujourd'hui, nous sommes le " + actu + ".<BR>");
document.write("Mon anniversaire est le: " + anni + ".</P>");
document.write("<B>Alors, il ne reste plus que " + intervalle + " jours avant mon anniversaire!</B>");

Et je le soumet à ESLint :

npx eslint test.js

E:\eslint\test.js
  1:1    error  Unexpected var, use let or const instead         no-var
  1:23   error  Expected linebreaks to be 'LF' but found 'CRLF'  linebreak-style
  2:1    error  Unexpected var, use let or const instead         no-var
  2:32   error  Expected linebreaks to be 'LF' but found 'CRLF'  linebreak-style
  3:1    error  Unexpected var, use let or const instead         no-var
  3:21   error  Unexpected string concatenation                  prefer-template
  3:21   error  Strings must use singlequote                     quotes
  3:43   error  Expected linebreaks to be 'LF' but found 'CRLF'  linebreak-style
  4:1    error  Unexpected var, use let or const instead         no-var
  4:50   error  Expected linebreaks to be 'LF' but found 'CRLF'  linebreak-style
  5:61   error  Expected linebreaks to be 'LF' but found 'CRLF'  linebreak-style
  6:16   error  Unexpected string concatenation                  prefer-template
  6:56   error  Strings must use singlequote                     quotes
  6:65   error  Expected linebreaks to be 'LF' but found 'CRLF'  linebreak-style
  7:16   error  Unexpected string concatenation                  prefer-template
  7:16   error  Strings must use singlequote                     quotes
  7:53   error  Strings must use singlequote                     quotes
  7:62   error  Expected linebreaks to be 'LF' but found 'CRLF'  linebreak-style
  8:16   error  Unexpected string concatenation                  prefer-template
  8:16   error  Strings must use singlequote                     quotes
  8:65   error  Strings must use singlequote                     quotes
  8:103  error  Newline required at end of file but not found    eol-last

✖ 22 problems (22 errors, 0 warnings)
  22 errors and 0 warnings potentially fixable with the `--fix` option.

On a une rafale d’erreurs. Mais la bonne nouvelle c’est qu’on peut tout corriger :

npx eslint test.js --fix

Résultat :

const actu = new Date();
const annee = actu.getFullYear();
const anni = new Date(`27 March, ${annee}`);
let intervalle = anni.getTime() - actu.getTime();
intervalle = Math.floor(intervalle / (1000 * 60 * 60 * 24));
document.write(`Aujourd'hui, nous sommes le ${actu}.<BR>`);
document.write(`Mon anniversaire est le: ${anni}.</P>`);
document.write(`<B>Alors, il ne reste plus que ${intervalle} jours avant mon anniversaire!</B>`);

Mais il est encore plus simple d’ajouter ESLint à un éditeur, par exemple Visual Studio Code :

Quand on l’installe on en dispose ensuite directement :

On peut facilement corriger :

Prettier

Un autre outil utile est Prettier. Vous verrez sur la page de l’outil qu’il est présenté comme « an opinionated code formatter ». Mais ça veut dire quoi ? Tout simplement que la mise en forme du code doit respecter les conventions.

On va prendre encore un script Javascript au hasard :

function verif_formulaire() {
  if (document.formulaire.nom.value == "") {
    alert("Veuillez entrer votre nom!");
    document.formulaire.nom.focus();
    return false;
  }
  var chkZ = 1;
  for (i = 0; i < document.formulaire.age.value.length; ++i)
    if (
      document.formulaire.age.value.charAt(i) < "0" ||
      document.formulaire.age.value.charAt(i) > "9"
    )
      chkZ = -1;
  if (chkZ == -1) {
    alert("Cette mention n'est pas un nombre!");
    document.formulaire.age.focus();
    return false;
  }
}

On va installer Prettier et l’utiliser avec notre script :

npm install --save-dev --save-exact prettier
npx prettier --write test.js

Et voilà le résultat :

function verif_formulaire()
{
 if(document.formulaire.nom.value == "")  {
   alert("Veuillez entrer votre nom!");
   document.formulaire.nom.focus();
   return false;
  }
 var chkZ = 1;
 for(i=0;i<document.formulaire.age.value.length;++i)
   if(document.formulaire.age.value.charAt(i) < "0"
   || document.formulaire.age.value.charAt(i) > "9")
     chkZ = -1;
 if(chkZ == -1) {
   alert("Cette mention n'est pas un nombre!");
   document.formulaire.age.focus();
   return false;
  }
}

Il y a plein d’options disponibles que vous pouvez trouver sur le site.

Les tests

Une approche à la mode est le TDD (Test Driven Development). Il consiste à commencer rpar écrire les tests, donc le résultat que l’on veut obtenir, puis ensuite le code jusqu’à satisfaire les tests. On procède généralement en 6 étapes :

  1. on écrit un test pour une nouvelle fonctionnalité
  2. on effectue le test qui doit logiquement échouer sauf si ce qu’on veut faire existe déjà !
  3. on écrit le code pour satisfaire le test
  4. on effectue le test et aussi tous les autres tests pour voir si on a rien cassé
  5. on peut refactoriser et nettoyer le code
  6. on écrit un nouveau test et c’est reparti !

C’est efficace mais devenir très lourd à gérer.

Les tests unitaires

Un test unitaire est une procédure pour vérifier qu’une partie d’un code fonctionne bien. Il existe de nombreux frameworks pour accomplir cette tâche : Mocha, Jasmine, Jest

Un des plus simples à utiliser est certainement Jest :

La première action consiste à l’installer :

npm install --save-dev jest

On va créer un module plusgrand.js avec une simple fonction :

function plusGrand(a, b) {
  return a > b
}
  
module.exports = plusGrand

On crée un fichier de test plusgrand.test.js :

const plusGrand = require('./plusgrand')

test('3 plus grand que 2 retourne true', () => {
  expect(plusGrand(3, 2)).toBe(true)
})

Il ne reste plus qu’à lancer le test :

Une bonne idée est de prévoir une déclaration de script dans package.json :

{
  "scripts": {
    "test": "jest"
  }
}

On peut alors lancer les tests avec :

npm run test

Vous pouvez trouver une documentation détaillée ici.

Les tests d’intégration

Faire des tests unitaires c’est bien, ça prouve que toutes les briques de notre application fonctionnent bien. Mais ça ne nous garantit pas que l’application va fonctionner parce que toutes ces briques doivent collaborer dans un ensemble plus vaste. C’est là qu’interviennent les tests d’intégration.

On va alors fairez appel à plusieurs modules, une base de données, des composants externes, des API… Forcément ces testst seront d’une exécution plus lentes et ne piurront pas être effectués trop souvent. D’autre part lorsque ce test échoue il est parfois difficile d’en trouver la cause alors qu’un test unitaire est plus ciblé.

Ces tests d’intégration peuvent aussi se faire avec Jest. Au lieu d’une seule action on va en faire plusieurs et faire intervenir plus de code dans le test.

Les tests End-to-End

Les tests d’intégration font intervenir plusieurs parties d’une application. Avec les tests End-to-End on monte encore d’un cran en utilisant l’ensemble de l’application. c’est en général le test final à effectuer pour être sûr que notre application rend bine le service qu’on avait prévu.

La librairie Puppeteer est généralement utilisée pour cest tests. Elle a été créée en 2018 par Google. Puppeteer est une version allégée de Chrome, en fait il n’y a pas d’interface. Mais on dispose d’un Chrome complet pour les tests.

Il faut commencer par l’installer :

npm install puppeteer

L’installation prend un peu de temps parce que la version la plus récente de Chromium est chargée.

On va faire un petit essai avec un fichier puppeteer.js :

const puppeteer = require('puppeteer'); // Là le point virgule est indispensable

(async () => {
  const browser = await puppeteer.launch()
  const page = await browser.newPage()
  await page.goto('https://codaholic.sillo.org/')
  await page.screenshot({path: 'example.png'})
  const link = await page.$('a:contains("Javascript")')
  link.click()
  await page.waitFor(3000)
  await page.screenshot({path: 'example.png'})
  await browser.close()
})()

On lance Chromium, on ouvre la page d’accueil de Codaholic, on clique sur le lien javascript, on attend 3 secondes, on prend une copie d’écran qu’on enregistre et on libère Chromium.

Ce n’est qu’un petit exemple, pour en savoir plus allez lire la documentation.

Conclusion

On a vu dans cet article quelques éléments qui permettent d’avoir du code plus propre et aussi comment effectuer des tests. Dans le prochain article je vais aborder la programmation fonctionnelle qui devient de plus en plus à la mode.

Laisser un commentaire