Testes de unidade

Depois de alterar ou adicionar o código, execute testes de unidade existentes e considere escrever mais. Todos os testes são executados nas versões descomprimidas do código.

Há dois conjuntos de testes de unidade: testes de JS e testes de gerador de blocos.

Testes JS

Os testes de 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.

Como executar testes

Nas amostras em bloco e em bloco, npm run test executará os testes de unidade. Em com bloqueio, isso também executará outros testes, como inspeção e compilação. Você pode Também abra tests/mocha/index.html em um navegador para executar todos os modelos mocha de forma interativa provas.

Testes de escrita

Usamos a interface Mocha TDD para executar testes. Os testes são organizados em conjuntos, que pode conter mais subpacotes e/ou testes. Geralmente, cada componente do Blockly (como toolbox ou workspace) tem um arquivo de teste com uma ou mais suítes. Cada suíte pode ter setup e teardown que será chamado antes e depois, respectivamente, de cada teste da Google Workspace.

Ajudantes de teste

Temos várias funções auxiliares específicas do Blockly que podem ser úteis a escrever testes. Estes podem ser encontrados em núcleo e em blockly-samples.

As funções auxiliares incluem sharedTestSetup e sharedTestTeardown, que são obrigatórios e precisam ser chamados antes e depois dos testes (consulte a seção "Requisitos").

sharedTestSetup:
  • Configura timers falsos de sinon (em alguns testes, você precisará usar this.clock.runAll).
  • O stub Blockly.Events.fire é acionado imediatamente (configurável).
  • Configura a limpeza automática de blockTypes definidos por defineBlocksWithJsonArray.
  • Declara algumas propriedades no contexto this que devem ser acessíveis:
    • this.clock (mas não deve ser restaurado, caso contrário causará problemas no sharedTestTeardown)
    • this.eventsFireStub
    • this.sharedCleanup (para ser usado com addMessageToCleanup e addBlockTypeToCleanup) (OBSERVAÇÃO: não é necessário usar addBlockTypeToCleanup se você definiu o bloco usando defineBlocksWithJsonArray)

A função tem um parâmetro options opcional para definir a configuração. No momento, ele é usado apenas para determinar se o stub Blockly.Events.fire será acionado imediatamente (será criado por padrão).

sharedTestTeardown:
  • Descarta o espaço de trabalho this.workspace (dependendo de onde 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 e addBlockTypeToCleanup.
  • Limpa todas as mensagens adicionadas usando addMessageToCleanup.

Requisitos de teste

  • Cada teste precisa chamar sharedTestSetup.call(this); como a primeira linha da configuração do pacote mais externo e sharedTestTeardown.call(this); como o última linha na eliminação do pacote mais externo de um arquivo.
  • Se você precisar de um espaço de trabalho com uma caixa de ferramentas genérica, use um dos caixas de ferramentas predefinidas no teste index.html. Veja abaixo um exemplo.
  • Descarte corretamente o this.workspace. Na maioria dos testes, você definir this.workspace no pacote mais externo e usá-lo para todas as mas, em alguns casos, é possível defini-los ou redefini-los em um conjunto interno Por exemplo, um dos testes exige um espaço de trabalho com opções diferentes do que o configurado 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 por sharedTestTeardown.
    • 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 chamando workspaceTeardown.call(this, this.workspace) no descarte desse pacote.
    • Se você definir this.workpace no pacote mais externo, mas redefinir em um pacote de testes interno, primeiro chame workspaceTeardown.call(this, this.workspace) antes de redefini-la para eliminar o espaço de trabalho original definido no pacote de nível superior. Você também precisa descartar manualmente o novo valor chamando workspaceTeardown.call(this, this.workspace) novamente na eliminação de essa suíte interna.

Estrutura de teste

Os testes de unidade geralmente seguem uma estrutura definida, que pode ser resumida como organizar, agir, afirmar.

  1. Organizar: configurar o estado do mundo e todas as condições necessárias para o comportamento em teste.
  2. Ação: chame o código em teste para acionar o comportamento que está sendo testado.
  3. 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 para organizar, e os ato e é possível combinar as etapas de declaração colocando a chamada em linha para o código em teste na declaração. Para casos mais complexos, seus testes serão mais legíveis se você seguir essas três etapas.

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 e teardown 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 corrompidos.
    • A ordem dos parâmetros é chai.assert.equal(actualValue, expectedValue, optionalMessage). Se você trocar actual e expected, as mensagens de erro não farão sentido.
  • O Sinon é usado para criar métodos stub 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 preocupamos com o modo como os resultados são usados pelo o método em teste. O Sinon cria um stub da função getMetrics para retornar uma que podem ser facilmente verificadas nas nossas declarações de teste.
  • Os métodos setup para cada pacote 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 de testes 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 () {
          // ...
        });
      });
    });
    

    Lembre-se de removê-los antes de confirmar seu código.

Testes do gerador de blocos

Cada bloco tem seus próprios testes de unidade. Esses testes verificam se os blocos geram do que as funções pretendidas.

  1. 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).
  2. Escolha a parte relevante do sistema para testar no menu suspenso e clique em "Carregar". Os blocos vão aparecer no espaço de trabalho.
  3. 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.
  4. Clique em "Python".
    Copie e execute o código gerado em um interpretador do Python. Se a saída terminar com "OK", o teste foi aprovado.
  5. 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.
  6. Clique em "Lua".
    Copie e execute o código gerado em um intérprete Lua. Se a saída terminar com "OK", o teste foi aprovado.
  7. Clique em "Dart".
    Copie e execute o código gerado em um interpretador do Dart (link em inglês). Se a saída terminar com "OK", o teste foi aprovado.

Como editar testes de gerador de blocos

  1. Carregue tests/generators/index.html em um navegador.
  2. Escolha a parte relevante do sistema no menu suspenso e clique em "Carregar". Os blocos vão aparecer no espaço de trabalho.
  3. Faça alterações ou adições nos blocos.
  4. Clique em "XML".
  5. Copie o XML gerado para o arquivo apropriado em tests/generators/.