Tests unitaires

Après avoir modifié ou ajouté du code, vous devez exécuter des tests unitaires existants et envisager d'en écrire d'autres. Tous les tests sont exécutés sur les versions non compressées du code.

Il existe deux ensembles de tests unitaires: les tests JS et les tests de générateur de blocs.

Tests JS

Les tests JS confirment le fonctionnement des fonctions JavaScript internes dans le cœur de Blockly. Nous utilisons Mocha pour exécuter des tests unitaires, Sinon pour bouchon les dépendances et Chai pour effectuer des assertions sur le code.

Exécution des tests

npm run test exécute les tests unitaires dans les exemples blockly et blockly. Dans le bloc par blocs, d'autres tests tels que le linting et la compilation seront également exécutés. Vous pouvez également ouvrir tests/mocha/index.html dans un navigateur pour exécuter de manière interactive tous les tests Mocha.

Écriture des tests

Nous utilisons l'interface Mocha TDD pour exécuter les tests. Les tests sont organisés en suites, qui peuvent contenir à la fois des sous-suites et/ou des tests supplémentaires. En règle générale, chaque composant de Blockly (tel que toolbox ou workspace) possède son propre fichier de test contenant une ou plusieurs suites. Chaque suite peut comporter une méthode setup et une méthode teardown qui seront appelées respectivement avant et après chaque test de cette suite.

Outils d'aide aux tests

Nous disposons d'un certain nombre de fonctions d'assistance propres à Blockly qui peuvent être utiles lors de l'écriture de tests. Vous les trouverez dans les ressources core et blockly-samples.

Les fonctions d'assistance incluent sharedTestSetup et sharedTestTeardown, qui doivent être obligatoires pour être appelées avant et après vos tests (voir la section "Exigences").

sharedTestSetup:
  • configure de faux minuteurs (dans certains tests, vous devrez utiliser this.clock.runAll) ;
  • Stub Blockly.Events.fire se déclenche immédiatement (configurable).
  • Configure le nettoyage automatique des blockTypes définis via defineBlocksWithJsonArray.
  • Déclare quelques propriétés sur le contexte this destinées à être accessibles :
    • this.clock (mais ne doit pas être restauré, sinon cela causera des problèmes dans sharedTestTeardown)
    • this.eventsFireStub
    • this.sharedCleanup (à utiliser avec addMessageToCleanup et addBlockTypeToCleanup) (REMARQUE: vous n'avez pas besoin d'utiliser addBlockTypeToCleanup si vous avez défini le bloc avec defineBlocksWithJsonArray)

La fonction comporte un paramètre options facultatif permettant de configurer la configuration. Actuellement, il sert uniquement à déterminer s'il faut que le bouchon Blockly.Events.fire se déclenche immédiatement (le bouchon par défaut).

sharedTestTeardown:
  • Supprime l'espace de travail this.workspace (selon l'endroit où il a été défini, consultez la section "Exigences relatives aux tests" pour en savoir plus).
  • Restaure tous les bouchons.
  • Nettoie tous les types de blocs ajoutés via defineBlocksWithJsonArray et addBlockTypeToCleanup.
  • Permet de nettoyer tous les messages ajoutés via addMessageToCleanup.

Conditions des tests

  • Chaque test doit appeler sharedTestSetup.call(this); en tant que première ligne de la configuration de la suite externe et sharedTestTeardown.call(this); en tant que dernière ligne de la suppression de la suite externe pour un fichier.
  • Si vous avez besoin d'un espace de travail avec une boîte à outils générique, vous pouvez utiliser l'une des boîtes à outils prédéfinies sur le index.html de test. Voir l'exemple ci-dessous.
  • Vous devez correctement éliminer this.workspace. Dans la plupart des tests, vous définirez this.workspace dans la suite la plus externe et l'utiliserez pour tous les tests ultérieurs, mais dans certains cas, vous pourrez le définir ou le redéfinir dans une suite interne (par exemple, l'un de vos tests nécessite un espace de travail avec des options différentes de celles que vous avez initialement configurées). Il doit être éliminé à la fin du test.
    • Si vous définissez this.workspace dans la suite externe et que vous ne la redéfinissez jamais, aucune autre action n'est requise. Il sera automatiquement supprimé par sharedTestTeardown.
    • Si vous définissez this.workspace pour la première fois dans une suite interne (c'est-à-dire que vous ne l'avez pas défini dans la suite la plus externe), vous devez le supprimer manuellement en appelant workspaceTeardown.call(this, this.workspace) lors de la suppression de cette suite.
    • Si vous définissez this.workpace dans la suite externe, puis que vous la redéfinissez dans une suite de tests interne, vous devez d'abord appeler workspaceTeardown.call(this, this.workspace) avant de le redéfinir pour supprimer l'espace de travail d'origine défini dans la suite de premier niveau. Vous devez également supprimer manuellement la nouvelle valeur en appelant à nouveau workspaceTeardown.call(this, this.workspace) lors de la suppression de cette suite interne.

Structure de test

Les tests unitaires suivent généralement une structure définie, qui peut être résumée ainsi : Organiser, agir, affirmer.

  1. Organisation: configurez l'état du monde et toutes les conditions nécessaires au comportement testé.
  2. Act: appelez le code testé pour déclencher le comportement testé.
  3. Assert (Assertion) : effectuez des assertions sur la valeur renvoyée ou les interactions avec les objets de simulation afin de vérifier l'exactitude.

Dans un test simple, il peut n'y avoir aucun comportement à organiser, et les phases d'action et d'assertion peuvent être combinées en intégrant l'appel au code testé dans l'assertion. Pour les cas plus complexes, vos tests seront plus lisibles si vous respectez ces trois étapes.

Voici un exemple de fichier de test (simplifié par rapport à la réalité).

suite('Flyout', function() {
  setup(function() {
    sharedTestSetup.call(this);
    this.toolboxXml = document.getElementById('toolbox-simple');
    this.workspace = Blockly.inject('blocklyDiv',
        {
          toolbox: this.toolboxXml
        });
  });

  teardown(function() {
    sharedTestTeardown.call(this);
  });

  suite('simple flyout', function() {
    setup(function() {
      this.flyout = this.workspace.getFlyout();
    });
    test('y is always 0', function() {
      // Act and assert stages combined for simple test case
      chai.assert.equal(this.flyout.getY(), 0, 'y coordinate in vertical flyout is 0');
    });
    test('x is right of workspace if flyout at right', function() {
      // Arrange
      sinon.stub(this.flyout.targetWorkspace, 'getMetrics').returns({
        viewWidth: 100,
      });
      this.flyout.targetWorkspace.toolboxPosition = Blockly.TOOLBOX_AT_RIGHT;
      this.flyout.toolboxPosition_ = Blockly.TOOLBOX_AT_RIGHT;

      // Act
      var x = this.flyout.getX();

      // Assert
      chai.assert.equal(x, 100, 'x is right of workspace');
    });
  });
});

Points à noter dans cet exemple:

  • Une suite peut contenir d'autres suites comportant des méthodes setup et teardown supplémentaires.
  • Chaque suite et chaque test ont un nom descriptif.
  • Les assertions Chai permettent d'effectuer des assertions sur le code.
    • Vous pouvez fournir un argument de chaîne facultatif qui sera affiché en cas d'échec du test. Cela facilite le débogage des tests défaillants.
    • L'ordre des paramètres est chai.assert.equal(actualValue, expectedValue, optionalMessage). Si vous permutez actual et expected, les messages d'erreur n'auront aucun sens.
  • Sinon, cette méthode permet de bouchon les méthodes lorsque vous ne souhaitez pas appeler le code réel. Dans cet exemple, nous ne voulons pas appeler la fonction de métriques réelle, car elle n'est pas pertinente pour ce test. Nous nous soucions uniquement de la façon dont les résultats sont utilisés par la méthode testée. Sinon, bouchon la fonction getMetrics pour renvoyer une réponse standardisée que nous pouvons facilement vérifier dans nos assertions de test.
  • Les méthodes setup de chaque suite ne doivent contenir que une configuration générique qui s'applique à tous les tests. Si un test pour un comportement particulier repose sur une certaine condition, cette condition doit être clairement indiquée dans le test correspondant.

Tests de débogage

  • Vous pouvez ouvrir les tests dans un navigateur et utiliser les outils pour les développeurs pour définir des points d'arrêt et déterminer si vos tests échouent de manière inattendue (ou échouent de manière inattendue).
  • Définissez .only() ou .skip() sur un test ou une suite pour exécuter uniquement cet ensemble de tests, ou ignorer un test. Exemple :

    suite.only('Workspace', function () {
      suite('updateToolbox', function () {
        test('test name', function () {
          // ...
        });
        test.skip('test I don’t care about', function () {
          // ...
        });
      });
    });
    

    N'oubliez pas de les supprimer avant de valider votre code.

Tests du générateur de blocs

Chaque bloc possède ses propres tests unitaires. Ces tests vérifient que les blocs génèrent du code que des fonctions comme prévu.

  1. Chargez tests/generators/index.html dans Firefox ou Safari. Notez que Chrome et Opera comportent des restrictions de sécurité qui empêchent le chargement des tests à partir du système local "file://" (problèmes 41024 et 47416).
  2. Dans le menu déroulant, choisissez la partie du système à tester, puis cliquez sur "Charger". Des blocs doivent apparaître dans l'espace de travail.
  3. Cliquez sur "JavaScript".
    Copiez et exécutez le code généré dans une console JavaScript. Si le résultat se termine par "OK", le test a réussi.
  4. Cliquez sur "Python".
    Copiez et exécutez le code généré dans un interpréteur Python. Si le résultat se termine par "OK", le test a réussi.
  5. Cliquez sur "PHP".
    Copiez et exécutez le code généré dans un interpréteur PHP. Si le résultat se termine par "OK", le test a réussi.
  6. Cliquez sur "Lua".
    Copiez et exécutez le code généré dans un interpréteur Lua. Si le résultat se termine par "OK", le test a réussi.
  7. Cliquez sur "Jeu de fléchettes".
    Copiez et exécutez le code généré dans un interpréteur Dart. Si le résultat se termine par "OK", le test a réussi.

Modification des tests du générateur de blocs

  1. Chargez tests/generators/index.html dans un navigateur.
  2. Choisissez la partie du système appropriée dans le menu déroulant, puis cliquez sur "Charger". Des blocs doivent apparaître dans l'espace de travail.
  3. Apportez les modifications souhaitées ou ajoutez les blocs.
  4. Cliquez sur "XML".
  5. Copiez le code XML généré dans le fichier approprié dans tests/generators/.