Test delle unità

Dopo aver modificato o aggiunto il codice, ti consigliamo di eseguire i test delle unità esistenti e valutare la possibilità di crearne altri. Tutti i test vengono eseguiti sulle versioni non compresse del codice.

Esistono due gruppi di test delle unità: test JS e test del generatore a blocchi.

Test JS

I test JS confermano il funzionamento delle funzioni JavaScript interne nel core di Blockly. Utilizziamo Mocha per eseguire i test delle unità, Sinon per stub delle dipendenze e Chai per fare asserzioni sul codice.

Test in esecuzione

Sia negli esempi a blocchi che in quelli a blocchi, npm run test eseguirà i test delle unità. In blocco, verranno eseguiti anche altri test, come analisi tramite lint 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 Mocha TDD per eseguire i test. I test sono organizzati in suite, che possono contenere sottosuite e/o test aggiuntivi. In genere, ogni componente di Blockly (come 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 nella suite.

Aiutanti per il test

Abbiamo una serie di funzioni helper specifiche di Blockly che possono essere utili durante la scrittura dei test. Sono disponibili in core e in blockly-samples.

Le funzioni helper includono sharedTestSetup e sharedTestTeardown, che devono essere chiamati prima e dopo i test (consulta la sezione Requisiti).

sharedTestSetup:
  • Configura timer sinon falsi (in alcuni test dovrai utilizzare this.clock.runAll).
  • Stubs Blockly.Events.fire si attivi immediatamente (configurabile).
  • Consente di configurare la pulizia automatica dei blockTypes definiti tramite defineBlocksWithJsonArray.
  • Dichiara nel contesto this alcune proprietà che dovrebbero essere accessibili:
    • this.clock (ma non deve essere ripristinata altrimenti causerà problemi in sharedTestTeardown)
    • this.eventsFireStub
    • this.sharedCleanup (da utilizzare con addMessageToCleanup e addBlockTypeToCleanup) (NOTA: non è necessario utilizzare addBlockTypeToCleanup se hai definito il blocco utilizzando defineBlocksWithJsonArray)

La funzione ha un parametro options facoltativo per configurare la configurazione. Attualmente, viene utilizzato solo per determinare se simulare Blockly.Events.fire affinché venga attivato immediatamente (lo stub viene attivato per impostazione predefinita).

sharedTestTeardown:
  • Consente di eliminare l'area di lavoro this.workspace (a seconda di dove è stata definita, consulta la sezione Requisiti per il test per saperne di più).
  • Ripristina tutti gli stub.
  • Ripulisce tutti i tipi di blocco aggiunti tramite defineBlocksWithJsonArray e addBlockTypeToCleanup.
  • Ripulisce tutti i messaggi aggiunti tramite addMessageToCleanup.

Requisiti per il test

  • Ogni test deve richiamare sharedTestSetup.call(this); come prima riga nella configurazione della suite più esterna e sharedTestTeardown.call(this); come ultima riga nella rimozione della suite più esterna per un file.
  • Se hai bisogno di un'area di lavoro con una serie di strumenti generici, puoi utilizzare una delle cassette degli strumenti preimpostate nel test index.html. Di seguito è riportato un esempio.
  • Devi smaltire correttamente this.workspace. Nella maggior parte dei test, definisci this.workspace nella suite più esterna e lo utilizzi per tutti i test successivi, ma in alcuni casi puoi definirlo o ridefinire in una suite interna (ad esempio, uno dei test richiede un'area di lavoro con opzioni diverse da quelle configurate inizialmente). Deve essere smaltito al termine del test.
    • Se definisci this.workspace nella suite più esterna e non lo ridefinisci mai, non sono necessarie ulteriori azioni. Verrà eliminato automaticamente da sharedTestTeardown.
    • Se definisci this.workspace per la prima volta in una suite interna (ovvero non lo hai definito nella suite più esterna), devi eliminarlo manualmente chiamando workspaceTeardown.call(this, this.workspace) nell'eliminazione di quella suite.
    • Se definisci this.workpace nella suite più esterna, ma poi lo ridefinisci in una suite di test interna, devi prima chiamare workspaceTeardown.call(this, this.workspace) prima di ridefinire la suite per eliminare l'area di lavoro originale definita nella suite di primo livello. Devi inoltre eliminare manualmente il nuovo valore chiamando di nuovo workspaceTeardown.call(this, this.workspace) nello smontaggio di questa suite interna.

Struttura di test

I test delle unità di solito seguono una struttura impostata, che può essere riassunta come disporre, agire, asserire.

  1. Disponi: imposta lo stato del mondo e le condizioni necessarie per il comportamento in fase di test.
  2. Agisci: chiama il codice sottoposto a test per attivare il comportamento da testare.
  3. Dichiarazione: fai delle asserzioni sul valore restituito o sulle interazioni con oggetti fittizi per verificarne la correttezza.

In un semplice test potrebbe non esserci alcun comportamento da organizzare e le fasi di azione e dichiarazione possono essere combinate incorporando la chiamata al codice in fase di test nell'assertion. Per i casi più complessi, i test saranno più leggibili se segui queste tre fasi.

Di seguito è riportato un file di test di esempio (semplificato rispetto a quello 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 da considerare in questo esempio:

  • Una suite può contenere altre suite che dispongono di metodi setup e teardown aggiuntivi.
  • Ogni suite e test ha un nome descrittivo.
  • Le asserzioni chai vengono utilizzate per fare asserzioni sul codice.
    • Puoi fornire un argomento stringa facoltativo che verrà visualizzato se il test ha esito negativo. In questo modo è più facile eseguire il debug dei test non funzionanti.
    • L'ordine dei parametri è chai.assert.equal(actualValue, expectedValue, optionalMessage). Se cambi actual e expected, i messaggi di errore non avranno senso.
  • Sinon viene utilizzato per eseguire la stub 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 i risultati vengono utilizzati dal metodo in fase di test. Sinon esegue lo stub della funzione getMetrics per restituire una risposta predefinita che possiamo facilmente verificare 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 condizione 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 i punti di interruzione e verificare se i test non riescono inaspettatamente (o passano inaspettatamente).
  • Imposta .only() o .skip() su un test o una suite per eseguire solo quell'insieme di test oppure ignora 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 confermare il codice.

Test generatori a blocchi

Ogni blocco ha i propri test delle unità. Questi test verificano che i blocchi generino codice anziché le funzioni come previsto.

  1. 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).
  2. Scegli la parte pertinente del sistema da testare dal menu a discesa e fai clic su "Carica". I blocchi dovrebbero essere visualizzati nello spazio di lavoro.
  3. Fai clic su "JavaScript".
    Copia ed esegui il codice generato in una console JavaScript. Se l'output termina con "OK", il test è stato superato.
  4. Fai clic su "Python".
    Copia ed esegui il codice generato in un interprete Python. Se l'output termina con "OK", il test è stato superato.
  5. Fai clic su "PHP".
    Copia ed esegui il codice generato in un interprete PHP. Se l'output termina con "OK", il test è stato superato.
  6. Fai clic su "Lua".
    Copia ed esegui il codice generato in un interprete Lua. Se l'output termina con "OK", il test è stato superato.
  7. Fai clic su "Dart".
    Copia ed esegui il codice generato in un interprete Dart. Se l'output termina con "OK", il test è stato superato.

Modifica dei test dei generatori a blocchi

  1. Carica tests/generators/index.html in un browser.
  2. Scegli la parte pertinente del sistema dal menu a discesa e fai clic su "Carica". I blocchi dovrebbero essere visualizzati nello spazio di lavoro.
  3. Apporta eventuali modifiche o aggiunte ai blocchi.
  4. Fai clic su "XML".
  5. Copia il codice XML generato nel file appropriato in tests/generators/.