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 ensharedTestTeardown
)this.eventsFireStub
this.sharedCleanup
(se usará conaddMessageToCleanup
yaddBlockTypeToCleanup
) (NOTA: No es necesario que usesaddBlockTypeToCleanup
si definiste el bloque condefineBlocksWithJsonArray
)
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
yaddBlockTypeToCleanup
. - 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 asharedTestTeardown.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ásthis.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 aworkspaceTeardown.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 aworkspaceTeardown.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 aworkspaceTeardown.call(this, this.workspace)
en la eliminación de este paquete interno.
- Si defines
Estructura de pruebas
Las pruebas de unidades suelen seguir una estructura establecida, que se puede resumir como organizar, actuar y afirmar.
- Arrange: Configura el estado general y las condiciones necesarias para el comportamiento que se está probando.
- Act: Llama al código que se está probando para activar el comportamiento que se está probando.
- 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
yteardown
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 intercambiasactual
yexpected
, 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.
- 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). - 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.
- 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ó. - 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ó. - 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ó. - 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ó. - 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
- Carga
tests/generators/index.html
en un navegador. - 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.
- Realiza los cambios o las incorporaciones que desees en los bloques.
- Haz clic en "XML".
- Copia el XML generado en el archivo correspondiente en
tests/generators/
.