Depois de mudar ou adicionar código, execute os testes de unidade existentes e considere programar mais. Todos os testes são executados nas versões descompactadas do código.
Há dois conjuntos de testes de unidade: testes JS e testes de gerador de blocos.
Testes JS
Os testes JS confirmam a operação de funções JavaScript internas no núcleo do Blockly. Usamos o Mocha para executar testes de unidade, o Sinon para simular dependências e o Chai para fazer declarações sobre o código.
Testes em execução
No Blockly e no blockly-samples, o npm run test
vai executar os testes de unidade. No
Blockly, isso também executa outros testes, como linting e compilação. Também é possível
abrir tests/mocha/index.html
em um navegador para executar interativamente todos os testes
do Mocha.
Como programar testes
Usamos a interface Mocha TDD para executar testes. Os testes são organizados em conjuntos,
que podem conter subconjuntos e/ou testes adicionais. Geralmente, cada
componente do Blockly (como toolbox
ou workspace
) tem o próprio arquivo de teste
que contém um ou mais pacotes. Cada conjunto pode ter um método setup
e teardown
que será chamado antes e depois, respectivamente, de cada teste nesse
conjunto.
Auxiliares de teste
Temos várias funções auxiliares específicas do Blockly que podem ser úteis ao escrever testes. Eles podem ser encontrados no núcleo e em amostras em bloco.
As funções auxiliares incluem sharedTestSetup
e sharedTestTeardown
, que
precisam ser chamadas antes e depois dos testes. Consulte a seção
"Requisitos".
sharedTestSetup
:
- Configura timers falsos do sinon (em alguns testes, será necessário usar
this.clock.runAll
). - Stubs Blockly.Events.fire para disparar imediatamente (configurável).
- Configura a limpeza automática de blockTypes definidos usando
defineBlocksWithJsonArray
. - Declara algumas propriedades no contexto
this
que devem ser acessíveis:this.clock
(mas não pode ser restaurado, caso contrário, causará problemas nosharedTestTeardown
)this.eventsFireStub
this.sharedCleanup
(para ser usado comaddMessageToCleanup
eaddBlockTypeToCleanup
) (OBSERVAÇÃO: não é necessário usaraddBlockTypeToCleanup
se você definiu o bloco usandodefineBlocksWithJsonArray
).
A função tem um parâmetro options
opcional para configurar a configuração. Atualmente, ele é usado apenas para determinar se o stub Blockly.Events.fire
precisa ser disparado imediatamente (será feito o stub por padrão).
sharedTestTeardown
:
- Descartes do espaço de trabalho
this.workspace
(dependendo de onde ele foi definido, consulte a seção "Requisitos de teste" para mais informações). - Restaura todos os stubs.
- Limpa todos os tipos de bloco adicionados por
defineBlocksWithJsonArray
eaddBlockTypeToCleanup
. - Limpa todas as mensagens adicionadas por
addMessageToCleanup
.
Requisitos de teste
- Cada teste precisa chamar
sharedTestSetup.call(this);
como a primeira linha na configuração do pacote mais externo esharedTestTeardown.call(this);
como a última linha na desmontagem do pacote mais externo de um arquivo. - Se você precisar de um espaço de trabalho com uma caixa de ferramentas genérica, use uma das
caixas de ferramentas predefinidas
no
index.html
de teste. Veja abaixo um exemplo. - Descarte corretamente o
this.workspace
. Na maioria dos testes, você definethis.workspace
no pacote mais externo e o usa em todos os testes posteriores, mas, em alguns casos, pode defini-lo ou redefini-lo em um pacote interno (por exemplo, um dos testes exige um espaço de trabalho com opções diferentes das que você configurou originalmente). Ele precisa ser descartado no final do teste.- Se você definir
this.workspace
no pacote mais externo e nunca o redefinir, não será necessário fazer mais nada. Ele será descartado automaticamente emsharedTestTeardown
. - Se você definir
this.workspace
pela primeira vez em um pacote interno (ou seja, se você não o tiver definido no pacote mais externo), será necessário descartá-lo manualmente chamandoworkspaceTeardown.call(this, this.workspace)
no descarte desse pacote. - Se você definir
this.workpace
no pacote mais externo, mas redefini-lo em um pacote de testes interno, primeiro chameworkspaceTeardown.call(this, this.workspace)
antes de redefini-lo para eliminar o espaço de trabalho original definido no pacote de nível superior. Você também precisa descartar manualmente o novo valor chamandoworkspaceTeardown.call(this, this.workspace)
novamente na eliminação desse pacote interno.
- Se você definir
Estrutura de teste
Os testes de unidade geralmente seguem uma estrutura definida, que pode ser resumida como organizar, agir, afirmar.
- Organizar: configurar o estado do mundo e todas as condições necessárias para o comportamento em teste.
- Ação: chame o código em teste para acionar o comportamento que está sendo testado.
- Assert: faça declarações sobre o valor de retorno ou as interações com objetos simulados para verificar a exatidão.
Em um teste simples, pode não haver nenhum comportamento a ser organizado, e os estágios "act" e "assert podem ser combinados ao incluir a chamada para o código em teste na declaração. Para casos mais complexos, seus testes serão mais legíveis se você seguir esses três estágios.
Confira um exemplo de arquivo de teste (simplificado).
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');
});
});
});
Observações sobre este exemplo:
- Uma suíte pode conter outras suítes com métodos
setup
eteardown
adicionais. - Cada conjunto e teste tem um nome descritivo.
- As declarações de Chai são usadas para fazer declarações sobre o código.
- Você pode fornecer um argumento de string opcional que será exibido se o teste falhar. Isso facilita a depuração de testes com falhas.
- A ordem dos parâmetros é
chai.assert.equal(actualValue, expectedValue, optionalMessage)
. Se você trocaractual
eexpected
, as mensagens de erro não vão fazer sentido.
- O Sinon é usado para criar stubs de métodos quando você não quer chamar o código real. Neste
exemplo, não queremos chamar a função de métricas reais porque ela
não é relevante para este teste. Só nos interessa como os resultados são usados pelo
método em teste. O Sinon substitui a função
getMetrics
para retornar uma resposta padrão que pode ser verificada facilmente nas nossas afirmações de teste. - Os métodos
setup
de cada conjunto precisam conter apenas a configuração genérica que se aplica a todos os testes. Se um teste para um comportamento específico depender de uma determinada condição, essa condição precisa ser claramente indicada no teste relevante.
Testes de depuração
- É possível abrir os testes em um navegador e usar as ferramentas para desenvolvedores para definir pontos de interrupção e investigar se os testes estão falhando ou passando de forma inesperada.
Defina
.only()
ou.skip()
em um teste ou pacote para executar apenas esse conjunto ou pular um teste. Exemplo:suite.only('Workspace', function () { suite('updateToolbox', function () { test('test name', function () { // ... }); test.skip('test I don’t care about', function () { // ... }); }); });
Não se esqueça de removê-los antes de confirmar o código.
Testes do gerador de blocos
Cada bloco tem os próprios testes de unidade. Esses testes verificam se os blocos geram código do que as funções pretendidas.
- Carregue
tests/generators/index.html
no Firefox ou Safari. O Chrome e o Opera têm restrições de segurança que impedem o carregamento dos testes do sistema local "file://" (problemas 41024 e 47416). - Escolha a parte relevante do sistema para testar no menu suspenso e clique em "Carregar". Os blocos devem aparecer no espaço de trabalho.
- Clique em "JavaScript".
Copie e execute o código gerado em um console JavaScript. Se a saída terminar com "OK", o teste foi aprovado. - Clique em "Python".
Copie e execute o código gerado em um intérprete de Python. Se a saída terminar com "OK", o teste foi aprovado. - Clique em "PHP".
Copie e execute o código gerado em um interpretador do PHP. Se a saída terminar com "OK", o teste foi aprovado. - Clique em "Lua".
Copie e execute o código gerado em um interpretador Lua. Se a saída terminar com "OK", o teste foi aprovado. - Clique em "Dart".
Copie e execute o código gerado em um intérprete Dart. Se a saída terminar com "OK", o teste foi aprovado.
Como editar testes do gerador de blocos
- Carregue
tests/generators/index.html
em um navegador. - Escolha a parte relevante do sistema no menu suspenso e clique em "Carregar". Os blocos devem aparecer no espaço de trabalho.
- Faça alterações ou adições nos blocos.
- Clique em "XML".
- Copie o XML gerado para o arquivo apropriado em
tests/generators/
.