Pruebas de unidades

Después de cambiar o agregar código, debes ejecutar pruebas de unidades existentes y considerar escribir más. Todas las pruebas se ejecutan en las versiones sin comprimir del código.

Existen dos conjuntos de pruebas de unidades: las de JS y las de generador de bloques.

Pruebas de JS

Las pruebas de JS confirman el funcionamiento de las funciones internas de JavaScript en el núcleo de Blockly. Usamos Mocha para ejecutar pruebas de unidades, Sinon para crear dependencias de stub y Chai para realizar aserciones sobre el código.

Ejecuta pruebas

En las muestras en bloque y en las que se encuentran en bloque, npm run test ejecutará las pruebas de unidades. En bloque, también se ejecutarán otras pruebas, como el análisis con lint y la compilación. También puedes abrir tests/mocha/index.html en un navegador para ejecutar de forma interactiva todas las pruebas de Mocha.

Escribe pruebas

Usamos la interfaz TDD de Mocha para ejecutar pruebas. Las pruebas se organizan en paquetes, que pueden contener subpaquetes adicionales o pruebas. En general, cada componente de Blockly (como toolbox o workspace) tiene su propio archivo de prueba que contiene uno o más paquetes. Cada paquete puede tener un método setup y teardown que se llamará antes y después de cada prueba de ese paquete, respectivamente.

Asistentes de prueba

Contamos con una serie de funciones auxiliares específicas de Blockly que pueden ser útiles a la hora de escribir pruebas. Se pueden encontrar en core y en blockly-samples.

Las funciones auxiliares incluyen sharedTestSetup y sharedTestTeardown, que son obligatorios para llamar antes y después de las pruebas (consulta la sección Requisitos).

sharedTestSetup:
  • Configura cronómetros falsos de Sinon (en algunas pruebas, deberás usar this.clock.runAll).
  • Establece Blockly.Events.fire para activar de inmediato (configurable).
  • Configura la limpieza automática de los blockTypes definidos mediante defineBlocksWithJsonArray.
  • Declara algunas propiedades en el contexto this que deben ser accesibles:
    • this.clock (pero no se debe restablecer, ya que causará problemas en sharedTestTeardown)
    • this.eventsFireStub
    • this.sharedCleanup (se usará con addMessageToCleanup y addBlockTypeToCleanup) (NOTA: No es necesario que uses addBlockTypeToCleanup si definiste el bloque con defineBlocksWithJsonArray)

La función tiene un parámetro options opcional para establecer la configuración. Por el momento, solo se usa para determinar si se debe activar de inmediato el stub de Blockly.Events.fire (que se usará como stub de forma predeterminada).

sharedTestTeardown:
  • Descarta el lugar de trabajo this.workspace (según dónde se haya definido, consulta la sección Requisitos de la prueba para obtener más información).
  • Restablece todos los stubs.
  • Borra todos los tipos de bloques agregados a través de defineBlocksWithJsonArray y addBlockTypeToCleanup.
  • Borra todos los mensajes que se hayan agregado a través de addMessageToCleanup.

Requisitos de la prueba

  • Cada prueba debe llamar a sharedTestSetup.call(this); como la primera línea en la configuración del conjunto más externo y a sharedTestTeardown.call(this); como la última línea del desmontaje del conjunto más externo de un archivo.
  • Si necesitas un lugar de trabajo con una caja de herramientas genérica, puedes usar una de las cajas de herramientas predeterminadas en el index.html de prueba. Consulta el ejemplo que aparece a continuación.
  • Debes desechar this.workspace correctamente. En la mayoría de las pruebas, definirás this.workspace en el conjunto más externo y lo usarás para todas las pruebas posteriores, pero, en algunos casos, puedes definirlo o redefinirlo en un conjunto interno (por ejemplo, una de tus pruebas requiere un lugar de trabajo con opciones diferentes a las que configuraste originalmente). Debe desecharse al final de la prueba.
    • Si defines this.workspace en el conjunto más externo y nunca lo redefines, no es necesario que realices ninguna otra acción. sharedTestTeardown lo eliminará automáticamente.
    • Si defines this.workspace por primera vez en un conjunto interno (es decir, no lo definiste en el conjunto más externo), debes descartarlo de forma manual llamando a workspaceTeardown.call(this, this.workspace) en el desmontaje de ese paquete.
    • Si defines this.workpace en el paquete más externo, pero luego lo redefines en un paquete de pruebas interno, primero debes llamar a workspaceTeardown.call(this, this.workspace) antes de redefinirlo para eliminar el lugar de trabajo original definido en el paquete de nivel superior. También debes eliminar manualmente el valor nuevo llamando a workspaceTeardown.call(this, this.workspace) nuevamente en el desmontaje de este paquete interno.

Estructura de la prueba

Las pruebas de unidades suelen seguir una estructura establecida, que se puede resumir como arrange, act, assert.

  1. Arrange: Configura el estado del mundo y las condiciones necesarias para el comportamiento que se está probando.
  2. Act: Llama al código que se está probando para activar el comportamiento que se está probando.
  3. Assert: Realiza aserciones sobre el valor que se muestra o las interacciones con los objetos simulados para verificar la precisión.

En una prueba simple, es posible que no haya ningún comportamiento que organizar, y las etapas de acción y aserción se pueden combinar si se integra la llamada al código en prueba en la aserción. En casos más complejos, tus pruebas serán más legibles si sigues estas 3 etapas.

Este es un archivo de prueba de ejemplo (simplificado de la situación real).

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');
    });
  });
});

Aspectos que debes tener en cuenta en este ejemplo:

  • Un paquete puede incluir otros conjuntos que tengan métodos setup y teardown adicionales.
  • Cada paquete y cada prueba tienen un nombre descriptivo.
  • Las aserciones de chai se usan para realizar aserciones sobre el código.
    • Puedes proporcionar un argumento de string opcional que se mostrará si falla la prueba. Esto facilita la depuración de pruebas dañadas.
    • El orden de los parámetros es chai.assert.equal(actualValue, expectedValue, optionalMessage). Si cambias actual y expected, los mensajes de error no tendrán sentido.
  • Sinon se usa para crear códigos auxiliares cuando no quieres llamar al código real. En este ejemplo, no queremos llamar a la función de métricas real porque no es relevante para esta prueba. Solo nos interesa cómo el método en prueba usa los resultados. Sinon agrega stubs en la función getMetrics para mostrar una respuesta estándar que podemos verificar fácilmente en nuestras aserciones de prueba.
  • Los métodos setup de cada paquete deben contener solo una configuración genérica que se aplique a todas las pruebas. Si la prueba de un comportamiento particular se basa en una condición determinada, esa condición debe declararse claramente en la prueba relevante.

Pruebas de depuración

  • Puedes abrir las pruebas en un navegador y usar las herramientas para desarrolladores a fin de establecer puntos de interrupción y también investigar si las pruebas fallan de forma inesperada (o si se aprueban).
  • Configura .only() o .skip() en una prueba o paquete para ejecutar solo ese conjunto de pruebas, o bien omitir una prueba. Por ejemplo:

    suite.only('Workspace', function () {
      suite('updateToolbox', function () {
        test('test name', function () {
          // ...
        });
        test.skip('test I don’t care about', function () {
          // ...
        });
      });
    });
    

    Recuerda quitarlos antes de confirmar el código.

Pruebas del generador de bloques

Cada bloque tiene sus propias pruebas de unidades. Estas pruebas verifican que los bloques generan código que las funciones según lo previsto.

  1. Carga tests/generators/index.html en Firefox o Safari. Ten en cuenta que Chrome y Opera tienen restricciones de seguridad que impiden cargar las pruebas desde el sistema local "file://" (errores 41024 y 47416).
  2. En el menú desplegable, elige la parte relevante del sistema que deseas probar y haz clic en "Load". Los bloques deberían aparecer en el lugar de trabajo.
  3. Haz clic en "JavaScript".
    Copia y ejecuta el código generado en una consola de JavaScript. Si el resultado termina con "OK", la prueba pasó.
  4. Haz clic en “Python”.
    Copia y ejecuta el código generado en un intérprete de Python. Si el resultado termina con "OK", significa que la prueba pasó.
  5. Haz clic en “PHP”.
    Copia y ejecuta el código generado en un intérprete de PHP. Si el resultado termina con "OK", significa que la prueba pasó.
  6. Haz clic en "Lua".
    Copia y ejecuta el código generado en un intérprete Lua. Si el resultado termina con "OK", significa que la prueba pasó.
  7. Haz clic en "Dart".
    Copia y ejecuta el código generado en un intérprete de Dart. Si el resultado termina con "OK", significa que la prueba pasó.

Cómo editar pruebas del generador de bloques

  1. Carga tests/generators/index.html en un navegador.
  2. Elige la parte relevante del sistema en el menú desplegable y haz clic en "Load". Los bloques deberían aparecer en el lugar de trabajo.
  3. Haz cambios o adiciones en los bloques.
  4. Haz clic en "XML".
  5. Copia el XML generado en el archivo correspondiente en tests/generators/.