A imagem geral do teste de unidade



Este não é um guia sobre quais caracteres você precisa inserir no editor de código para obter testes de unidade. Isso é alimento para a mente, que deve ser consumido antes de tomar essas ações.

O tópico do teste de unidade não é tão simples quanto parece. Muitos de nós, desenvolvedores, realizamos testes de unidade sob pressão de clientes, funcionários, colegas, seus ídolos e assim por diante. Compreendemos rapidamente seu valor e, depois de concluir os preparativos técnicos, esquecemos o quadro geral, se é que o compreendemos. Neste artigo, falarei brevemente sobre o que é o teste de unidade e o que não é, tanto em geral quanto em PHP, e ao mesmo tempo descreverei o local em que o teste de unidade é necessário.

O que está testando?


Antes de se aprofundar nos testes de unidade, você precisa estudar a teoria dos testes para não cometer erros como os cometidos pelos autores de uma das estruturas PHP mais populares: eles mostraram testes de integração em seu site e os chamaram de testes de unidade. Não, Laravel, estes não são testes de unidade. Embora isso não me impeça de continuar amando essa estrutura.

Teste de software é definido como "uma investigação realizada para fornecer às partes interessadas informações sobre a qualidade do produto". Isso se opõe a "o teste de software é um desperdício do orçamento do projeto por desenvolvedores que não fazem nada importante e pedem mais tempo e dinheiro, porque" nada "pode ​​ser muito caro". Nada de novo aqui.

Aqui está minha breve história de se tornar um teste:

  • 1822 - Motor de diferença (Charles Babbage).
  • 1843 - Motor analítico (Ada Lovelace).
  • 1878 - Edison introduz o termo "bug".
  • 1957 - Teste e depuração de programas (Charles Baker).
  • 1958 - A primeira equipe de teste de software (Gerald Weinberg).
  • 1968 - Crise PO (Friedrich Bauer).
  • Década de 1970 - Modelo em cascata, modelo relacional, decomposição, análise crítica ( Passo a passo ), design e inspeção de código, qualidade e métricas, padrões de design.
  • Década de 1980 - Análise CRUD, arquitetura do sistema, autoteste, modelo V, confiabilidade, custo da qualidade, métodos de uso, padrões de projeto OOP.
  • Anos 90 - Scrum, teste de usabilidade, MoSCoW, teste heurístico, automação e teste de software.

Se você se relaciona com uma geração de millennials como eu, pode se surpreender ao saber que as equipes de teste existiam MUITO antes de você nascer. Pare por um momento, inspire, expire, acalme-se.
O histórico mostra como o tipo de teste considerado "bom o suficiente" para as partes interessadas mudou ao longo do tempo. Fases aproximadas que foram guiadas durante o teste:

  • ... - depuração de 1956
  • 1957 - 1978 Demonstração
  • 1979 - 1982 destruição
  • Estimativa 1983 - 1987
  • 1988 - ... prevenção

Portanto, o teste de unidade é necessário para evitar discrepâncias entre o projeto e a implementação.

O que está testando realmente?


Existem diferentes classificações de teste de software. Para entender melhor o local dos testes de unidade, mencionarei apenas as abordagens mais difundidas.

Os testes são: estático e dinâmico, “caixa” (caixa branca, caixa preta, caixa cinza), níveis e tipos. Cada abordagem usa diferentes critérios de classificação.

Teste estático e dinâmico


O teste estático é executado sem execução de código. Isso inclui revisão, verificação, revisão de código (ao observar o trabalho de outra programação / par), análise crítica, inspeções etc.

O teste dinâmico para obter os resultados corretos requer execução de código. Por exemplo, para testes de unidade , integração, sistema, aceitação e outros testes. Ou seja, o teste é realizado usando dados dinâmicos, entrada e saída.

Abordagem em caixa


De acordo com essa abordagem, todos os testes de software são divididos em três tipos de caixas:

  • O teste de caixa branca verifica estruturas e módulos internos, ignora a funcionalidade esperada para os usuários finais. Isso pode ser teste de API, injeção de falha, teste de unidade , teste de integração.
  • O teste da caixa preta está mais interessado no que o software faz, e não em como ele o faz. Isso significa que os testadores não precisam entender o objeto de teste ou entender como ele funciona sob o capô. Esse tipo de teste é direcionado aos usuários finais, interagindo com uma interface visível. As caixas-pretas incluem teste baseado em modelo, teste de uso, tabelas de transição de estado, teste de especificação, etc.
  • Os testes do tipo " caixa cinza " são projetados com o conhecimento de algoritmos de software e estruturas de dados (caixa branca), mas são realizados no nível do usuário (caixa preta). Isso inclui teste de regressão e teste de padrão.

Agora, para confundi-lo, direi que o teste de unidade também pode ser aplicado à "caixa preta", pois você pode entender o módulo em teste, mas não o sistema inteiro. Embora para mim ainda seja uma "caixa branca", e sugiro que você concorde com isso.

Níveis de teste


Seu número varia, geralmente na faixa de 4 a 6, e todos são úteis. Os nomes também podem ser diferentes, dependendo da cultura adotada pela empresa, você pode conhecer os testes de "integração" como "funcionais", os testes de "sistema" como "automatizados" e assim por diante. Para simplificar, descreverei 5 níveis:

  1. Teste de unidade
  2. Teste de integração.
  3. Testando interfaces de componentes.
  4. Teste de sistema.
  5. Teste de aceitação operacional.

O teste de unidade testa a funcionalidade de um pedaço de código específico, geralmente uma função por vez. O teste de integração verifica as interfaces entre os componentes para que os módulos montados juntos formem um sistema que funcione conforme o esperado. Este é um ponto importante, porque um grande número de testes, chamados de testes de unidade, são na verdade testes de integração e os desenvolvedores os consideram módulos. Se você pretende usar vários módulos - isso está testando a integração entre eles, e não os próprios módulos. O teste das interfaces dos componentes verifica os dados transferidos entre os diferentes módulos. Por exemplo, recebemos dados do módulo 1 - verificado - transferido para o módulo 2 - verificado. O teste do sistema é um teste de ponta a ponta para verificar a conformidade com todos os requisitos. O teste de aceitação operacional é realizado para verificar a prontidão operacional. Não é funcional, apenas a capacidade de manutenção dos serviços é verificada, se algum subsistema danifica o ambiente e outros serviços.

Tipos de teste


Cada tipo de teste, independentemente do seu nível, também pode ser dividido em outros tipos. Existem mais de 20 tipos comuns. O mais comum:

  • Teste de regressão .
  • Teste de aceitação.
  • Teste de fumaça
  • Uat
  • Ensaios destrutivos .
  • Teste de desempenho.
  • Teste contínuo .
  • Teste de usabilidade.
  • Teste de segurança.

Pelo nome, fica claro por que esse ou aquele tipo de teste se destina. Negrito são os testes de unidade em PHP. Se você realmente quiser, poderá aplicar cada um desses termos ao teste de unidade. No entanto, a principal variedade de testes de unidade são os testes de regressão, que verificam se todos os módulos do sistema são executados corretamente após fazer alterações no código.

Agora você sabe que os testes de unidade são dinâmicos, pertencem à classe "caixa branca", são executados no nível do módulo, são testes de regressão, mas os testes modulares podem ser entendidos como muitos tipos de testes. Então, o que são realmente testes de unidade?

O que é teste de unidade?


Um modelo em V é uma representação gráfica dos níveis acima, tipos e sua finalidade no ciclo de vida de desenvolvimento de software.



Após verificar e aprovar os requisitos detalhados do produto, quando começaram a escrever o código, os testes de unidade se tornaram a primeira linha de defesa contra qualquer inconsistência. Portanto, as empresas que entendem o que estão fazendo estão forçando os desenvolvedores a usar testes de unidade ou mesmo TDD, já que é muito mais barato corrigir erros nos estágios iniciais do que nos posteriores.

E isso é justo. Os testes de unidade têm muitas vantagens. Eles são:

  • Isole cada parte do programa e verifique sua correção.
  • Ajude a detectar problemas mais cedo.
  • Eles fazem os desenvolvedores pensarem em termos de entrada, saída e condições erradas.
  • Eles dão ao código uma aparência conveniente para testes, facilitam a refatoração futura.
  • Simplifique a integração dos módulos de trabalho (!).
  • Substitua parcialmente a documentação técnica.
  • Forçado a separar a interface da implementação.
  • Eles provam que o código do módulo funciona conforme o esperado (pelo menos matematicamente).
  • Pode ser usado como suítes de teste de regressão de baixo nível.
  • Demonstrar progresso na integração incompleta do sistema.
  • Reduza o custo de correção de bugs (com TDD - ainda mais).
  • Eles permitem melhorar a arquitetura do aplicativo, determinando a responsabilidade dos módulos.
  • Se você puder testá-lo, poderá se conectar ao seu sistema.
  • Teste de unidade é divertido!

No entanto, existem algumas limitações em que você pensou, provavelmente ao ler esta lista:

  • O teste de unidade não captura erros de integração.
  • Cada expressão booleana requer pelo menos dois testes e o número cresce rapidamente.
  • Os testes de unidade são tão problemáticos quanto o código que eles testam.
  • Vincular testes a algumas estruturas ou bibliotecas específicas pode limitar o fluxo de trabalho.
  • A maioria dos testes é escrita após a conclusão do desenvolvimento. Isso é triste Use TDD!
  • Talvez após uma pequena refatoração, o sistema funcione como antes, mas os testes falharão.
  • O custo do desenvolvimento está crescendo.
  • Erro humano: comentando sobre testes quebrados.
  • Erro humano: adicionando soluções alternativas ao código especificamente para passar nos testes de unidade.

Este último me mata mais. (Quase) em cada projeto, diretamente no código fonte do aplicativo de trabalho, encontro linhas como “se for um teste de unidade, carregue um banco de dados SQLite substituto, caso contrário, carregue outro banco de dados” ou “se for um teste de unidade, não envie um email, caso contrário enviar ”e assim por diante. Se seu aplicativo tiver uma arquitetura ruim, não finja que você pode consertar um software ruim com uma boa aprovação no teste, pois isso não melhorará.

Frequentemente discuti com colegas e clientes o que é um bom teste de unidade. Ele:

  • Rápido.
  • Automatizado.
  • Controla totalmente todas as suas dependências.
  • Confiável: pode ser iniciado em qualquer ordem, independentemente de outros testes.
  • Só pode ser executado na memória (sem interações com o banco de dados, leituras / gravações no sistema de arquivos).
  • Sempre retorna um único resultado.
  • Conveniente para leitura e acompanhamento.
  • Não testa a configuração do SUT (sistema em teste).
  • Possui uma ÚNICA TAREFA claramente definida.
  • É bem nomeado (e compreensível o suficiente para evitar a depuração apenas para descobrir o que está falhando).

Para quem sorriu depois de ler “automatizado”: ​​não quis dizer integrar o PHPUnit ou o JUnit nos pipelines de CI. O ponto é que, se você alterar o código, salve-o e não saiba se os módulos passam nos testes, eles não são automatizados, mas devem. A opção vencedora é o rastreamento de arquivos.

O que deve ser submetido a testes de unidade?


Em sistemas normais, os testes de unidade precisam ser escritos para:

  • Módulos - partes indivisíveis do sistema que executam qualquer tarefa (função, método, classe).
  • Métodos públicos.
  • Métodos protegidos, mas apenas em casos raros e quando ninguém vê.
  • Erros e suas correções.

A definição de um teste de unidade depende do desenvolvedor que escreveu o código. No PHP, quase sempre é um método ou função de classe, porque é um software indivisível que faz sentido por si só . Várias vezes vi como os desenvolvedores usavam uma matriz de miniclasses de um método como um único módulo. Isso faz sentido se a funcionalidade mínima exigir vários objetos.

Então você mesmo pode determinar o que é um módulo para você. Ou você pode testar os métodos um por um, facilitando a vida para aquele cara que trabalhará com o código.

Se você não realizar testes de unidade, proponho fazer isso após o próximo grande erro. Verifique com qual método ele será associado, escreva um teste com falha com os argumentos e resultados corretos, corrija o erro, execute o teste de unidade novamente. Se for aprovado, você pode ter certeza de que esse bug teve que ser corrigido pela última vez (levando em consideração seus cenários de entrada específicos).

Essa abordagem facilita o teste de unidade. Analise cada método separadamente. Os provedores de dados podem ajudar a determinar a entrada e a saída de qualquer cenário que possa vir à sua mente; portanto, aconteça o que acontecer, você saberá o que esperar.

O que NÃO precisa ser testado


É um pouco mais difícil determinar que você não precisa testar. Tentei compilar uma lista de elementos que não precisam ser submetidos a testes de unidade:

  • Funcionalidade fora do escopo dos módulos (!)
  • Integração de módulos com outros módulos (!)
  • Comportamento não isolado (dependências incontroláveis, bancos de dados reais, rede)
  • Métodos privados e seguros.
  • Métodos estáticos.
  • Bibliotecas externas.
  • Sua estrutura.

Estou certo de que o teste de unidade não deve ser aplicado a nenhuma das opções acima, exceto para métodos estáticos. Eu gosto de argumentar que estática, em essência, significa processualidade e, em muitos casos, processualidade é global. Se o método estático chamar outro método estático, essa dependência não poderá ser substituída. Isso significa que agora você está testando isoladamente. E então isso não é mais teste de unidade. Por outro lado, essa é a parte do código que pode viver por si só, tem um propósito e precisa ser testada para garantir que, independentemente de qual parte desse sistema estúpido a parte testada do código chame, ela não será quebrada. Portanto, acredito que você pode testar métodos estáticos se tiver certeza de que a saída do seu teste não pode ser alterada por nenhum outro teste e que a linguagem ou estrutura permita que você teste nativamente.

Como escrever testes de unidade?


  • Escreva o código adequado para o teste de unidade e, em seguida, teste-o.
  • Escreva o código adequado para o teste de unidade e, em seguida, teste-o.
  • Escreva o código adequado para o teste de unidade e, em seguida, teste-o.

Se "testá-lo" não for suficiente, o laracasts.com tem vídeos muito bons sobre o teste de unidade PHP. Existem muitos sites dedicados à mesma tarefa em outros idiomas. Não vejo razão para explicar como faço o teste de unidade, porque as ferramentas mudam rapidamente e, quando você lê este texto, posso mudar do PHPUnit para o Kahlan. Ou não Quem sabe

Mas responder à primeira pergunta (como escrever código adequado para testes de unidade) é muito mais fácil, e é improvável que a situação mude muito com o tempo:

  • SÓLIDO
  • SECO
  • A falta de novas palavras-chave no construtor.
  • A ausência de loops no construtor (e transições, se especificado).
  • Falta de métodos estáticos, parâmetros, classes.
  • Falta de métodos setup (): os objetos devem ser totalmente inicializados após a construção.
  • A falta de singleton (status global) e outros antipadrões não testáveis.
  • A falta de objetos onipotentes (objetos de Deus).
  • Falta de classes com funcionalidade mista (classes de preocupação mista).
  • Sem dependências ocultas.

Agora, sabendo o que são os testes de unidade e o que não são, o que você precisa e o que não precisa testar, em que local os testes de unidade ocupam o ciclo de vida de desenvolvimento de software, será mais fácil implementá-los. Resta encontrar uma estrutura ou biblioteca ao seu gosto. Em caso de dúvida, use a estrutura / linguagem padrão de fato.

Em conclusão: os testes de unidade são muito importantes para desenvolvedores e negócios. Eles precisam ser escritos, existem métodos comprovados que ajudarão você a cobrir facilmente os módulos com testes, principalmente pela preparação dos próprios módulos. Mas todas essas técnicas não fazem sentido sem o conhecimento da teoria de testes descrita neste artigo. Você precisa distinguir testes de unidade de testes de outros tipos. E quando você tiver um entendimento claro de sua cabeça, ficará muito mais fácil escrever testes.

Source: https://habr.com/ru/post/pt412695/


All Articles