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 danssharedTestTeardown
)this.eventsFireStub
this.sharedCleanup
(à utiliser avecaddMessageToCleanup
etaddBlockTypeToCleanup
) (REMARQUE: vous n'avez pas besoin d'utiliseraddBlockTypeToCleanup
si vous avez défini le bloc avecdefineBlocksWithJsonArray
)
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
etaddBlockTypeToCleanup
. - 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 etsharedTestTeardown.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éfinirezthis.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é 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 le supprimer manuellement en appelantworkspaceTeardown.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 appelerworkspaceTeardown.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 à nouveauworkspaceTeardown.call(this, this.workspace)
lors de la suppression de cette suite interne.
- Si vous définissez
Structure de test
Les tests unitaires suivent généralement une structure définie, qui peut être résumée ainsi : Organiser, agir, affirmer.
- Organisation: configurez l'état du monde et toutes les conditions nécessaires au comportement testé.
- Act: appelez le code testé pour déclencher le comportement testé.
- 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
etteardown
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 permutezactual
etexpected
, 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.
- 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 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 le résultat 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 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 le résultat se termine par "OK", le test a réussi. - 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
- Chargez
tests/generators/index.html
dans un navigateur. - 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.
- Apportez les modifications souhaitées ou ajoutez les blocs.
- Cliquez sur "XML".
- Copiez le code XML généré dans le fichier approprié dans
tests/generators/
.