Après avoir modifié ou ajouté du code, nous vous recommandons d'exécuter des tests unitaires existants et d'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 JavaScript confirment le fonctionnement des fonctions JavaScript internes dans le noyau de Blockly. Nous utilisons Mocha pour exécuter des tests unitaires, Sinon pour les dépendances de bouchon et Chai pour effectuer des assertions sur le code.
Exécution des tests
Dans Blockly et blockly-samples, npm run test
exécute les tests unitaires. Dans Blockly, d'autres tests tels que l'analyse lint et la compilation sont é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.
Écrire 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 des méthodes setup
et teardown
qui sont appelées avant et après, respectivement, chaque test de cette suite.
Assistants de test
Il existe un certain nombre de 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 être appelées avant et après vos tests (voir la section "Conditions requises").
sharedTestSetup
:
- Configure les faux minuteurs sans risque (dans certains tests, vous devrez utiliser
this.clock.runAll
). - Stubs Blockly.Events.fire se déclenche immédiatement (configurable).
- Configure le nettoyage automatique des blockTypes définis dans
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 à l'aide dedefineBlocksWithJsonArray
)
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 tous 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);
comme première ligne de la configuration de la suite la plus externe etsharedTestTeardown.call(this);
comme dernière ligne de la déconstruction de la suite la plus 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. 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 externe et l'utiliserez pour tous les tests suivants, 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 configurées initialement). 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 le redéfinissez dans une suite de tests interne, vous devez d'abord appelerworkspaceTeardown.call(this, this.workspace)
avant de le redéfinir afin de 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 être résumée comme suit : arranger, agir, affirmer.
- Organiser: configurez l'état du monde et toutes les conditions nécessaires au comportement testé.
- Agir: appelez le code testé pour déclencher le comportement testé.
- 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 peut n'y avoir aucun comportement à organiser, et les étapes 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 vous en tenez à 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 qui disposent de méthodes
setup
etteardown
supplémentaires. - Chaque suite et chaque test ont un nom descriptif.
- Les assertions Chai permettent de faire des assertions sur le code.
- Vous pouvez fournir un argument de chaîne facultatif qui s'affichera en cas d'échec du test. Cela facilite le débogage des 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 permet de créer des bouchons de méthode lorsque vous ne souhaitez pas appeler le code réel. 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 manière dont les résultats sont utilisés par la méthode testée. Sinon, il remplace la fonction
getMetrics
pour renvoyer une réponse standard 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 de développement pour définir des points d'arrêt et déterminer 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 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 dispose de ses propres tests unitaires. Ces tests vérifient que les blocs génèrent du code plutôt que des fonctions comme prévu.
- 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). - Dans le menu déroulant, choisissez la partie pertinente du système à tester, puis cliquez sur "Charger". Des blocs doivent apparaître 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 par "OK", le test a réussi. - Cliquez sur "Python".
Copiez et exécutez le code généré dans un interpréteur Python. Si la sortie 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 la sortie 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 la sortie 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". Des blocs doivent apparaître dans l'espace de travail.
- Apportez les modifications ou ajouts souhaités aux blocs.
- Cliquez sur "XML".
- Copiez le fichier XML généré dans le fichier approprié de
tests/generators/
.