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 danssharedTestTeardown
)this.eventsFireStub
this.sharedCleanup
(à utiliser avecaddMessageToCleanup
etaddBlockTypeToCleanup
) (REMARQUE: vous n'avez pas besoin d'utiliseraddBlockTypeToCleanup
si vous avez défini le bloc en utilisantdefineBlocksWithJsonArray
)
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
etaddBlockTypeToCleanup
. - 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 etsharedTestTeardown.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éfinirezthis.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 parsharedTestTeardown
- 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 appelantworkspaceTeardown.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 appelerworkspaceTeardown.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 à nouveauworkspaceTeardown.call(this, this.workspace)
lors du démontage de cette suite interne.
- Si vous définissez
Structure des tests
Les tests unitaires suivent généralement une structure définie, qui peut se résumer en organiser, agir, affirmer.
- Organisation: permet de définir l'état du monde et les conditions requises pour le comportement testé.
- Action : appelez le code à tester pour déclencher le comportement à tester.
- 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 desteardown
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 échangezactual
etexpected
, 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.
- 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). - 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.
- 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. - 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. - 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. - 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. - 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
- Chargez
tests/generators/index.html
dans un navigateur. - 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.
- Apportez des modifications ou des ajouts aux blocs.
- Cliquez sur "XML".
- Copiez le code XML généré dans le fichier approprié dans
tests/generators/
.