Dopo aver modificato o aggiunto codice, devi eseguire i test delle unità esistenti e valutare la possibilità di scriverne altri. Tutti i test vengono eseguiti sulle versioni non compresse del codice.
Esistono due insiemi di test delle unità: test JS e test del generatore di blocchi.
Test JS
I test JS confermano il funzionamento delle funzioni JavaScript interne nel nucleo di Blockly. Utilizziamo Mocha per eseguire i test delle unità, Sinon per stubre le dipendenze e Chai per creare asserzioni relative al codice.
Test in esecuzione
Sia negli esempi a blocchi che in quelli a blocchi, npm run test
eseguirà i test delle unità. In
blockly, verranno eseguiti anche altri test come linting e compilazione. Puoi anche aprire tests/mocha/index.html
in un browser per eseguire in modo interattivo tutti i test mocha.
Test di scrittura
Utilizziamo l'interfaccia TDD di Mocha per eseguire i test. I test sono organizzati in suite,
che possono contenere sia sottosuite aggiuntive e/o test. In genere, ogni componente di Blockly (ad esempio toolbox
o workspace
) ha il proprio file di test che contiene una o più suite. Ogni suite può avere un metodo setup
e teardown
che verrà chiamato rispettivamente prima e dopo ogni test della
suite.
Componenti di supporto per i test
Abbiamo una serie di funzioni di supporto specifiche per Blockly che possono essere utili per scrivere i test. Puoi trovarli in core e in blockly-samples.
Le funzioni helper includono sharedTestSetup
e sharedTestTeardown
,
che devono essere chiamate prima e dopo i test (consulta la sezione Requisiti).
sharedTestSetup
:
- Configura timer falsi di sinon (in alcuni test dovrai utilizzare
this.clock.runAll
). - Blocca Blockly.Events.fire in modo che si attivi immediatamente (configurabile).
- Configura la pulizia automatica dei tipi di blocco definiti tramite
defineBlocksWithJsonArray
. - Dichiara alcune proprietà del contesto
this
che dovrebbero essere accessibili:this.clock
(ma non deve essere ripristinato, altrimenti causerà problemi insharedTestTeardown
)this.eventsFireStub
this.sharedCleanup
(da utilizzare conaddMessageToCleanup
eaddBlockTypeToCleanup
) (NOTA: non è necessario utilizzareaddBlockTypeToCleanup
se hai definito il blocco utilizzandodefineBlocksWithJsonArray
)
La funzione ha un parametro facoltativo options
per configurare la configurazione. Al momento, viene utilizzato solo per determinare se attivare o meno lo stub Blockly.Events.fire
immediatamente (verrà attivato per impostazione predefinita).
sharedTestTeardown
:
- Gestisce l'area di lavoro
this.workspace
(a seconda di dove è stata definita, consulta la sezione Requisiti di test per ulteriori informazioni). - Ripristina tutti gli stub.
- Ripulisce tutti i tipi di blocchi aggiunti tramite
defineBlocksWithJsonArray
eaddBlockTypeToCleanup
. - Consente di eliminare tutti i messaggi aggiunti tramite
addMessageToCleanup
.
Requisiti per i test
- Ogni test deve chiamare
sharedTestSetup.call(this);
come prima riga nel setup della suite più esterna esharedTestTeardown.call(this);
come ultima riga nel teardown della suite più esterna per un file. - Se hai bisogno di uno spazio di lavoro con una cassetta degli attrezzi generica, puoi utilizzare una delle cassettine degli attrezzi predefinite nel test
index.html
. Di seguito è riportato un esempio. - Devi smaltire correttamente
this.workspace
. Nella maggior parte dei test, definiraithis.workspace
nella suite più esterna e la utilizzerai per tutti i test successivi, ma in alcuni casi potresti definirla o ridefinirla in una suite interna (ad esempio, uno dei tuoi test richiede uno spazio di lavoro con opzioni diverse da quelle che hai configurato inizialmente). Deve essere smaltito al termine del test.- Se definisci
this.workspace
nella suite più esterna e non la ridefinisci mai, non sono necessarie ulteriori azioni. Verrà smaltito automaticamente dasharedTestTeardown
. - Se definisci
this.workspace
per la prima volta in una suite interna (ovvero non lo hai definito nella suite più esterna), devi eliminarlo manualmente chiamandoworkspaceTeardown.call(this, this.workspace)
durante lo smantellamento della suite. - Se definisci
this.workpace
nella suite più esterna, ma poi la ridefinisci in una suite di test interna, devi innanzitutto chiamareworkspaceTeardown.call(this, this.workspace)
prima di ridefinire l'ambiente per eliminare l'area di lavoro originale definita nella suite di primo livello. Devi anche eliminare manualmente il nuovo valore chiamando di nuovoworkspaceTeardown.call(this, this.workspace)
nel teardown di questa suite interna.
- Se definisci
Struttura del test
I test di unità in genere seguono una struttura fissa, che può essere riassunta come organizza, esegui, verifica.
- Organizza: configura lo stato del mondo e le eventuali condizioni necessarie per il comportamento in fase di test.
- Azione: chiama il codice in test per attivare il comportamento in esame.
- Assert: effettua affermazioni sul valore restituito o sulle interazioni con oggetti simulati al fine di verificarne la correttezza.
In un test semplice, potrebbe non essere necessario organizzare alcun comportamento e le fasi di azione e verifica possono essere combinate inserendo in linea la chiamata al codice in test nella verifica. Per i casi più complessi, i test saranno più leggibili se attieniti a queste tre fasi.
Ecco un esempio di file di test (semplificato rispetto al file reale).
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');
});
});
});
Aspetti importanti di questo esempio:
- Una suite può contenere altre suite con metodi
setup
eteardown
aggiuntivi. - Ogni suite e ogni test ha un nome descrittivo.
- Le asserzioni Chai vengono utilizzate per fare affermazioni sul codice.
- Puoi fornire un argomento stringa facoltativo che verrà visualizzato se il test non va a buon fine. In questo modo è più facile eseguire il debug dei test non funzionanti.
- L'ordine dei parametri è
chai.assert.equal(actualValue, expectedValue, optionalMessage)
. Se scambiactual
eexpected
, i messaggi di errore non avranno senso.
- Sinon viene usato per eseguire lo stubing dei metodi quando non si vuole chiamare il codice reale. In questo esempio, non vogliamo chiamare la funzione delle metriche reali perché non è pertinente per questo test. Ci interessa solo come vengono utilizzati i risultati dal metodo in prova. Sinon sostituisce la funzione
getMetrics
in modo da restituire una risposta predefinita che possiamo verificare facilmente nelle nostre asserzioni di test. - I metodi
setup
per ogni suite devono contenere solo la configurazione generica che si applica a tutti i test. Se un test per un determinato comportamento si basa su una determinata condizione, questa deve essere chiaramente indicata nel test pertinente.
Test di debug
- Puoi aprire i test in un browser e utilizzare gli strumenti per sviluppatori per impostare punti di interruzione e verificare se i test non superano inaspettatamente (o se vengono superati inaspettatamente).
Imposta
.only()
o.skip()
su un test o una suite per eseguire solo quel determinato insieme di test o salta un test. Ad esempio:suite.only('Workspace', function () { suite('updateToolbox', function () { test('test name', function () { // ... }); test.skip('test I don’t care about', function () { // ... }); }); });
Ricordati di rimuoverli prima di eseguire il commit del codice.
Test del generatore di blocchi
Ogni blocco ha i propri test di unità. Questi test verificano che i blocchi generino codice che funzioni come previsto.
- Carica
tests/generators/index.html
in Firefox o Safari. Tieni presente che Chrome e Opera hanno limitazioni di sicurezza che impediscono il caricamento dei test dal sistema locale "file://" (problemi 41024 e 47416). - Scegli la parte pertinente del sistema da testare dal menu a discesa e fai clic su "Carica". Nell'area di lavoro dovrebbero essere visualizzati i blocchi.
- Fai clic su "JavaScript".
Copia ed esegui il codice generato in una console JavaScript. Se l'output termina con "OK", il test è stato superato. - Fai clic su "Python".
Copia ed esegui il codice generato in un interprete Python. Se l'output termina con "OK", il test è stato superato. - Fai clic su "PHP".
Copia ed esegui il codice generato in un interprete PHP. Se l'output termina con "OK", il test è stato superato. - Fai clic su "Lua".
Copia ed esegui il codice generato in un interprete Lua. Se l'output termina con "OK", il test è stato superato. - Fai clic su "Dart".
Copia ed esegui il codice generato in un interprete Dart. Se l'output termina con "OK", il test è stato superato.
Modificare i test del generatore di blocchi
- Carica
tests/generators/index.html
in un browser. - Scegli la parte pertinente del sistema dal menu a discesa e fai clic su "Carica". I blocchi dovrebbero apparire nell'area di lavoro.
- Apporta eventuali modifiche o aggiunte ai blocchi.
- Fai clic su "XML".
- Copia il codice XML generato nel file appropriato in
tests/generators/
.