Caixa Branca

A técnica de teste de caixa branca (white-box testing) tem como objetivo avaliar a estrutura interna dos elementos de programação (técnica estrutural) durante a etapa de Codificação nas fases de testes unitários (unit testing) - responsáveis por auxiliar e avaliar no desenvolvimento dos cenários de uma unidade de código (estrutura interna do programa) e/ou testes de integração ou componentes (integration testing or component testing) - responsáveis por avaliar a comunicação entre unidades e recursos, principalmente a camada de infraestrutura e códigos de terceiros (verificação).

Durante o desenvolvimento de software, os testes auxiliam nas técnicas de:

  • Projeto de Programa Sistemático - Systematic Program Design (SPD);
  • Desenvolvimento Guiado por Testes - Test Driven Development (TDD);
  • Refatoração - Refactoring;
  • Cobertura de Testes - Test Coverage;
  • Integração Contínua - Continuous Integration (CI); e
  • Testes de Regressão - Regression Testing.

Outras características apresentadas são:

  • Implementação: das funcionalidades do cenário;
  • FeedBack: notificação dos problemas no código;
  • Bugs: previne e corrige os problemas; e
  • Debugging: reduz a necessidade de uso.

O Teste

O teste utiliza as tarefas do cenário para a composição de um conjunto de casos de teste durante a etapa de Codificação, assim descrevendo o que cada estrutura de programação deve fazer para garantir a verificação da Arquitetura.

Na elaboração dos casos de teste utiliza uma identificação e um nome descritivo para cada variação de uma específica funcionalidade do cenário - formando uma execução atômica, assim definindo seus dados de testes como entrada e sua saída esperada na execução, conforme o estado inicial.

{ ID; Nome do Teste; Entrada; Saída Esperada; Estado Inicial }

Exemplo:

| ID  | Nome do Teste   | Entrada    | Saída Esperada | Estado Inicial            |
|-----+-----------------+------------+----------------+---------------------------|
|  1  | Testando o A... | "variável" | "variável"     | Objeto Contador existente |
|     |                 | 20         | 123            |                           |
|-----+-----------------+------------+----------------+---------------------------|
|  2  | Testando o B... | N/A        | "variável"     | Objeto Contador com valor |
|     |                 |            | 345            | padrão de 345             |
|-----+-----------------+------------+----------------+---------------------------|
| ... |                 |            |                |                           |

Os casos de teste são organizadas em quatro (4) fases/etapas, sendo:

  • setup ou arrange: construção e configuração dos estados (entradas, condições e limites) requeridos pelo caso de teste;
  • exercise ou act: execução das funcionalidades do código sobre teste;
  • verify ou assert: avaliação dos estados e comportamentos do código, comparando os resultados obtidos com as saídas esperadas; e/ou
  • teardown ou after: liberação dos recursos utilizados pelo código.

Importante o cuidado para não violar o Princípio da Responsabilidade Única - Single Responsibility Principle (SRP) durante a implementação do código sobre teste, pois deve-se testar apenas as funcionalidades expostas (Tell, Don't Ask) sem o encadeamento de mensagens (Law of Demeter), a proposta básica é que cada Classe de Equivalência deva possuir um caso de teste único e suficiente, ou seja, a inclusão de pequenos incrementos entre testes conforme suas condições limites.

O Que Testar?

As tecnologias de desenvolvimento possuem elementos de programação mínimos legíveis aos testes, sendo:

  • Java: Classes e Métodos;
  • Haskell: Funções;
  • Lisp: Funções, Comandos e Macros; e
  • C: Funções.

Documentação

Os testes formam a documentação viva do código, em que são de extrema importância seus identificadores (nomes dos testes) serem pensadas em nível sintático (relação entre os termos) e semântico (sentido de ideias) com o que será testado.

Convenções

As seguintes convenções para os identificadores são utilizados:

  • Casos de Testes:

    • NameTest: testes unitários;
    • NameIntegrationTest: testes de integração; e
    • NameSystemTest: testes de sistema (ver Caixa Preta).
  • Testes:

    • Action: utiliza uma ação avaliável do teste, considerando o resultado e/ou condição da operação, exemplo: adicionaUmUsuarioEmUmaListaVazia;
    • Given-When-Then: relaciona o contexto, ação e resultado do teste, exemplo: dadoXQuandoAcontecerYEntaoFacaZ; e
    • Helper Objects: nomeia os comandos em modo imperativo e as avaliações em modo indicativo, exemplo: dispareUmSinalDeFalha e mostraAOcorrenciaDaFalha.

Dicas

Os identificadores tendem a ficarem longo, assim dificultando em sua leitura - uma dica é utilizar underscore sobre o camel case:

requisitarUmaMensagemComRepositorioVazio para requisitar_uma_mensagem_com_repositorio_vazio

Feedbacks

Os excessos no código de teste (padrões de feedback) diz o quanto estável é a unidade de programação:

  • exercícios das responsabilidades: baixa coesão;
  • dublês das colaboradoras: alto acoplamento; e
  • avaliação em colaboradoras: encapsulamento.

Complexidade ciclomática: métrica das ramificações existentes em uma unidade de programação, quanto maior o número, mais complexo e difícil de ser testado será o software (mais informações em Medindo a complexidade do seu código).

Ferramentas

As ferramentas mais utilizadas são:

  • assert
  • JUnit
  • Hamcrest
  • Mockito

O que pensar?

  • AAA[A] - organização e formatação dos casos de teste:

    Arrange: monta/organiza o código com todas as entradas e pré-condições necessárias ao teste;

    Act: exercita/executa o código sobre teste;

    Assert: avalia os resultados obtidos do código com os esperados pelo teste;

    After: finaliza os recursos utilizados no teste.

  • FIRST - propriedades de bons testes:

    Fast: rápidos em sua execução, assim mais testes serão avaliados;

    Isolated: isolados dos outros códigos, assim a verificação da falha será óbvia;

    Repeatable: possui repetibilidade em qualquer ordem e tempo;

    Self-validating: testes autoavaliáveis, automação em sua execução e avaliação;

    Timely: em momento oportuno, escreva os testes antes do código.

  • R->BICEP - questões sobre os testes:

    Right: os resultados estão certos?;

    Boundary: as condições limites estão corretas? Essa é verificada através do CORRECT;

    Inverse: os relacionamentos inversos (operações inversas) estão sendo utilizadas?;

    Cross-Check: uso de checagem cruzada com outros métodos de mesmo significado estão sendo utilizadas?;

    Error Conditions: as condições de erros estão sendo forçadas?;

    Performance: o desempenho está dentro dos limites?.

  • CORRECT - as condições limites estão:

    Conformance: com os valores conforme o formato especificado?

    Ordering: com o conjunto de valores em uma ordem determinada?

    Range: com os valores dentro da faixa (mínimo e máximo) especificada?

    Reference: com alguma referência externa fora do controle do código?

    Existence: com valor existente (nulo, zero, presente e etc...)?

    Cardinality: com a quantidade de números suficiente? Os casos interessantes são Zero, Um e Muitos através da regra 0-1-n.

    Time: com as temporizações corretas (sincronização, tempo, eventos, referência e etc...)?

Boas Práticas

  • Identificação: ver Documentação.
  • Refatoração: importante até mesmo para o código de teste.
  • Baby Steps: começando pelo cenário de teste mais simples em pequenos incrementos, assim ganhando confiança e conhecimento do sistema.
  • Test Data Builders: utilização do padrão de projeto (GoF) Builder para o processo de criação dos objetos para os cenários de testes - auxilia no problema de duplicação de código.
  • Adapters: utilização para testes com métodos estáticos e códigos legados.
  • Test Double: isolação e simplificação das funcionalidades com dependência externa, assim avaliando as características de estado e comportamento do objeto:
    • Dummy: implementa uma versão simples do código, visando a compilação e/ou execução;
    • Fake: simplifica (com atalhos e simulação) a utilização dos recursos e dependências necessários para o código de produção, útil para prototyping e spikes em memória;
    • Stubs: implementa uma Query - responde com valores predefinidos (hardcoded), sem o uso de dados reais e livre de efeitos colaterais;
    • Saboteurs: em edição;
    • Mocks: registra e avalia as expectativas do comportamento da comunicação, simulando os recursos e dependências; e
    • Spy: registra a utilização da comunicação - estatístico.
  • Infraestrutura: a camada de infraestrututra (DAO, Messages...) é testada com testes de integração.
  • Execute Around Method: utilização para testes com avaliação de exceção.

Test Smell

Código de teste também possui bad smells, tais como:

  • Unnecessary Test Code (Código de Teste Desnecessário): o caso de teste é construído com código em excesso e/ou defensivo, sem significado e valor ao teste;
  • Missing Abstractions (Falta de Abstração): exagero e/ou preciosismo nos detalhes sobre a organização e formatação do teste;
  • Irrelevant Information (Informação Irrelevante): utilização de dados irrelevantes ao teste, tais como literais e sentinelas;
  • Bloated Construction (Construção Inchada): complexidade na montagem dos elemento no teste;
  • Multiple Assertions (Múltiplas Avaliações): excesso de responsabilidades no teste;
  • Irrelevant Details in Test (Detalhes Irrelevante no Teste): uso de recursos desnecessários ao teste, tais como serviços transversais de segurança, logging e etc...;
  • Misleading Organization (Organização Enganosa): falta de organização no código de teste, por não responder a questão AAA;
  • Implicit Meaning (Significado Implícito): excesso de dados e informações aos elementos (constantes, variáveis, dados e etc...) do teste;

Exceções

@Test:expected em edição

@Rules em edição

Try-Catch em edição

EAM em edição

results matching ""

    No results matching ""