Tests unitaires

Après avoir modifié ou ajouté du code, vous devez exécuter les 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 JavaScript et les tests du générateur de blocs.

Tests JS

Les tests JS confirment le fonctionnement des fonctions JavaScript internes dans le de base. Nous utilisons Mocha pour exécuter des tests unitaires, Sinon pour créer des bouchons de dépendances et Chai pour émettre des assertions sur le code.

Exécution des tests

Dans les échantillons par blocs et par blocs, npm run test exécute les tests unitaires. Dans de façon bloc, d'autres tests, tels que le linting et la compilation, seront aussi 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 général, chaque composant de Blockly (comme toolbox ou workspace) possède son propre fichier de test contenant une ou plusieurs suites. Chaque suite peut avoir un setup et un teardown qui sera appelée avant et après, respectivement, chaque test dans ce suite.

Assistants de test

Nous proposons plusieurs fonctions d'assistance spécifiques à Blockly qui peuvent être utiles lors de l'écriture de tests. Vous les trouverez dans core et dans blockly-samples.

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

sharedTestSetup :
  • Configure des faux minuteurs sinon (dans certains tests, vous devrez utiliser this.clock.runAll).
  • Stubs Blockly.Events.fire doit se déclencher immédiatement (configurable).
  • Configure le nettoyage automatique des types de blocs définis via defineBlocksWithJsonArray.
  • Déclaration de quelques propriétés sur le contexte this qui sont censées être accessibles :
    • this.clock (mais ne doit pas être restauré, sinon cela entraînera 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 en utilisant defineBlocksWithJsonArray)

La fonction comporte un paramètre options facultatif pour configurer la configuration. Actuellement, il n'est utilisé que pour déterminer si Blockly.Events.fire doit être instancié immédiatement (par défaut).

sharedTestTeardown :
  • Élimine l'espace de travail this.workspace (selon l'emplacement où il a été défini, consultez la section "Exigences de test" pour en savoir plus).
  • Restaure toutes les bouchons.
  • Nettoie tous les types de blocs ajoutés via defineBlocksWithJsonArray et addBlockTypeToCleanup.
  • Nettoie tous les messages ajoutés via addMessageToCleanup.

Exigences concernant les tests

  • Chaque test doit appeler sharedTestSetup.call(this); sur la première ligne du de la suite externe et sharedTestTeardown.call(this); en tant que dernière ligne de la suppression de la suite de plus haut niveau 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. Vous trouverez un exemple ci-dessous.
  • Vous devez supprimer this.workspace comme il se doit. Dans la plupart des tests, vous définirez this.workspace dans la suite la plus externe et l'utiliserez pour tous les tests suivants. Toutefois, dans certains cas, vous pouvez 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 jeté à la fin du test.
    • Si vous définissez this.workspace dans la suite la plus externe et ne la redéfinissez jamais, aucune autre action n'est requise. Elle sera automatiquement supprimée 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 vous en débarrasser manuellement en appelant workspaceTeardown.call(this, this.workspace) lors du démontage de cette suite.
    • Si vous définissez this.workpace dans la suite de plus haut niveau, puis que vous redéfinissez dans une suite de tests interne, vous devez d'abord appeler workspaceTeardown.call(this, this.workspace) avant de la 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 du démontage de cette suite interne.

Structure des tests

Les tests unitaires suivent généralement une structure définie, qui peut se résumer en organiser, agir, affirmer.

  1. Organisation: permet de définir l'état du monde et les conditions requises pour le comportement testé.
  2. Action : appelez le code à tester pour déclencher le comportement à tester.
  3. Assert : effectuez des assertions sur la valeur renvoyée ou les interactions avec des objets simulés afin de vérifier leur exactitude.

Dans un test simple, il n'y a peut-être pas de comportement à organiser, et les étapes d'action et d'assertion peuvent être combinées en insérant l'appel du 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 concernant cet exemple:

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

Tests de débogage

  • Vous pouvez ouvrir les tests dans un navigateur et utiliser les outils pour les développeurs pour définir et déterminez si vos tests échouent de manière inattendue (ou réussissent de manière inattendue !).
  • Définissez .only() ou .skip() sur un test ou une suite pour n'exécuter que cet ensemble de tests. ou d'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 code que les fonctions prévues.

  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 fichier local "file://" (problèmes 41024 et 47416).
  2. Dans le menu déroulant, choisissez la partie du système à tester en fonction de votre situation. cliquez sur "Charger". Les blocages doivent s'afficher dans l'espace de travail.
  3. Cliquez sur "JavaScript".
    Copiez et exécutez le code généré dans une console JavaScript. Si la sortie se termine avec "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 la sortie se termine par "OK", le test a réussi.
  7. Cliquez sur "Dart".
    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.

Modifier les tests du générateur de blocs

  1. Chargez tests/generators/index.html dans un navigateur.
  2. Sélectionnez la partie du système concernée dans le menu déroulant, puis cliquez sur "Charger". Les blocages doivent s'afficher dans l'espace de travail.
  3. Apportez des modifications ou des ajouts aux blocs.
  4. Cliquez sur "XML".
  5. Copiez le code XML généré dans le fichier approprié dans tests/generators/.