Pruebas de unidades

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

Existen dos conjuntos de pruebas de unidades: pruebas de JS y pruebas del 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 stubs de dependencias y Chai para hacer aserciones sobre el código.

Ejecución de pruebas

Tanto en las muestras bloqueadas como en las de bloques, npm run test ejecutará las pruebas de unidades. En Blockly, también se ejecutarán otras pruebas, como linting y compilación. También puedes abrir tests/mocha/index.html en un navegador para ejecutar de forma interactiva todas las pruebas de mocha.

Pruebas de escritura

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

Asistentes de prueba

Tenemos varias funciones auxiliares específicas de Blockly que pueden ser útiles cuando se escriben pruebas. Puedes encontrarlas en core y en blockly-samples.

Las funciones auxiliares incluyen sharedTestSetup y sharedTestTeardown, a las que se debe 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).
  • Genera un stub de Blockly.Events.fire para que se active de inmediato (configurable).
  • Establece la limpieza automática de blockTypes definidos a través de defineBlocksWithJsonArray.
  • Declara algunas propiedades en el contexto this a las que se puede acceder:
    • this.clock (pero no debe restablecerse, 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 configurar la configuración. Actualmente, solo se usa para determinar si se debe crear un stub de Blockly.Events.fire para que se active de inmediato (se creará un stub de forma predeterminada).

sharedTestTeardown:
  • Eliminación del lugar de trabajo this.workspace (según dónde se haya definido, consulta la sección Requisitos de prueba para obtener más información).
  • Restablece todos los stubs.
  • Limpia todos los tipos de bloques agregados a través de defineBlocksWithJsonArray y addBlockTypeToCleanup.
  • Limpia todos los mensajes agregados 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 paquete más externo y a sharedTestTeardown.call(this); como la última línea en el desmontaje del paquete más externo para 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 siguiente ejemplo.
  • Debes desechar this.workspace de forma adecuada. En la mayoría de las pruebas, definirás this.workspace en el paquete más externo y lo usarás para todas las pruebas posteriores, pero en algunos casos puedes definirlo o redefinirlo en un paquete interno (por ejemplo, una de tus pruebas requiere un espacio de trabajo con opciones diferentes a las que configuraste originalmente). Se debe desechar al final de la prueba.
    • Si defines this.workspace en el paquete más externo y nunca lo redefines, no es necesario que realices ninguna otra acción. sharedTestTeardown se encargará de eliminarlo automáticamente.
    • Si defines this.workspace por primera vez en un paquete interno (es decir, no lo definiste en el paquete más externo), debes eliminarlo 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 vuelves a definir en un paquete de pruebas internas, primero debes llamar a workspaceTeardown.call(this, this.workspace) antes de redefinir para eliminar el espacio de trabajo original definido en el paquete de nivel superior. También debes eliminar manualmente el valor nuevo. Para ello, vuelve a llamar a workspaceTeardown.call(this, this.workspace) en la eliminación de este paquete interno.

Estructura de pruebas

Las pruebas de unidades suelen seguir una estructura establecida, que se puede resumir como organizar, actuar y afirmar.

  1. Arrange: Configura el estado general 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 afirmaciones sobre el valor que se muestra o las interacciones con objetos simulados para verificar la exactitud.

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

A continuación, se muestra un archivo de prueba de ejemplo (simplificado de la versió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:

  • Una suite puede contener otras suites que tengan métodos setup y teardown adicionales.
  • Cada suite y prueba tiene un nombre descriptivo.
  • Las aserciones de Chai se usan para hacer aserciones sobre el código.
    • Puedes proporcionar un argumento de cadena opcional que se mostrará si la prueba falla. Esto facilita la depuración de pruebas dañadas.
    • El orden de los parámetros es chai.assert.equal(actualValue, expectedValue, optionalMessage). Si intercambias actual y expected, los mensajes de error no tendrán sentido.
  • Sinon se usa para crear stubs de métodos 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 importa cómo el método en prueba usa los resultados. Sinon realiza un stub 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 suite deben contener solo la configuración genérica que se aplica a todas las pruebas. Si una prueba de un comportamiento en particular se basa en una condición determinada, esa condición debe indicarse claramente en la prueba relevante.

Pruebas de depuración

  • Puedes abrir las pruebas en un navegador y usar las herramientas para desarrolladores para establecer puntos de interrupción y analizar si las pruebas fallan de forma inesperada (o si aprueban de forma inesperada).
  • Establece .only() o .skip() en una prueba o un conjunto para ejecutar solo ese conjunto de pruebas o 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 tu código.

Pruebas del generador de bloques

Cada bloque tiene sus propias pruebas de unidad. Estas pruebas verifican que los bloques generen código que funcione 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://" (problemas 41024 y 47416).
  2. Elige la parte relevante del sistema que deseas probar en el menú desplegable y haz clic en "Cargar". 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 se aprobó.
  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 finalizó.
  5. Haz clic en “PHP”.
    Copia y ejecuta el código generado en un intérprete de PHP. Si el resultado termina con "OK", la prueba se aprobó.
  6. Haz clic en “Lua”.
    Copia y ejecuta el código generado en un intérprete de Lua. Si el resultado termina con "OK", significa que la prueba finalizó.
  7. Haz clic en “Dart”.
    Copia y ejecuta el código generado en un intérprete de Dart. Si el resultado termina con "OK", la prueba se aprobó.

Cómo editar pruebas del generador de bloqueos

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