Os testes unitários são essenciais. Sem eles, você está pisando sobre casca de ovos o tempo todo.
Isso é o que você ouve, pelo menos.
Mas por que isso acontece? E o que isso significa, afinal?
Vamos mergulhar no assunto.
O teste unitário consiste em verificar o comportamento das menores unidades em sua aplicação.
Tecnicamente, isso seria uma classe ou até mesmo um método de classe em línguas orientadas a objetos, e seria um procedimento ou função em línguas processuais e funcionais.
Funcionalmente, pode ser um conjunto de classes intimamente relacionadas. Como um “Cervo” e suas classes de suporte ”Cabeça”, “Rabo” e “Movimento”.
O que você considera uma unidade de trabalho é o que faz sentido para você. Não há regras rígidas e claras. Desde que isso o ajude a entender e pensar sobre sua aplicação.
Isso é diferente daquilo que é um teste unitário.
Michael Feathers (autor de “Working Effectively with Legacy Code”) colocou a distinção no nível do processo e do sistema.
Os testes unitários precisam funcionar isoladamente porque precisam funcionar rapidamente.
Todo o conjunto de testes unitários de uma aplicação precisa funcionar em minutos, de preferência em segundos. Você verá o porquê mais tarde.
É por isso que os testes unitários não podem utilizar nenhum processo ou sistema externo. Nenhuma operação de E/S (input/output) de qualquer tipo (banco de dados, arquivo, console, rede) exceto o registro de falhas nos testes e talvez a leitura da configuração padrão de alternância de recursos no início de uma execução de teste.
Se seu código de teste (ou as bibliotecas que utiliza) fizer E/S ou acessar qualquer coisa fora de seu processo, não é um teste unitário, e sim um teste de integração.
Muitos dizem que o objetivo dos testes unitários é validar que cada unidade de trabalho se comporta como projetada, esperada ou pretendida. E que você pode executar seus testes unitários toda vez que alguém faz uma mudança. Com apenas o apertar de um botão.
Mas o verdadeiro propósito do teste unitário é fornecer-lhe feedback quase instantâneo sobre o projeto e a implementação de seu código.
A criação de testes unitários, ou melhor, a facilidade ou dificuldade com que você pode criá-los lhe diz exatamente o quão testável é seu código. O quão bem você o projetou. E se você escutá-los, ao invés de fazer gambiarras para superar qualquer dificuldade, todos ganham.
Veja como.
Criar testes unitários é o mesmo que desenvolver qualquer código, mas há uma diferença.
Você cria um código de aplicação funcional para resolver um problema para seus clientes.
Você cria testes unitários para resolver os problemas que surgem no desenvolvimento do código da aplicação.
Adivinhe o que isso significa.
Sim, você é seu próprio cliente! E, claro, você quer tornar sua vida o mais fácil possível
Portanto, aproveite ao máximo essas melhores práticas.
Portanto, aproveite ao máximo essas melhores práticas.
Quando um teste depende de como outro teste mudou o ambiente (valores de variáveis, conteúdo de coleções, etc.), você terá dificuldade em acompanhar as condições iniciais para cada teste. Mais importante ainda, quando você obtiver resultados inesperados, você sempre se perguntará se as condições do teste ou o código de produção os causaram.
Use métodos de configuração e classes de utilidade aninhadas, se desejar, mas evite hierarquias de classes de teste e classes de utilidade geral. Isso evitará que você tenha que caçar através de diversas classes base ou unidades de classe de utilidade para encontrar os valores que um teste utiliza.
Quando uma ação deve ter mais de um efeito, teste cada um deles em um método diferente. Quando você se sentir tentado a usar mais de uma afirmação, pergunte-se se você está afirmando o fato mais significativo.
Resistir ao impulso de usar gambiarras e “automágica”. As gambiarras só abordam os sintomas, e a automágica reduz a transparência necessária para descobrir por que um teste que deveria ser bem-sucedido é reprovado ou, pior ainda, por que um teste que deveria ser reprovado é bem-sucedido.
Enfrente o que está causando a dor usando os meios mais diretos possíveis. Com bastante frequência, você pode fazer mudanças simples para tornar pelo menos parte de uma classe mais testável. “Working effectively with Legacy Code” de Michael Feathers é um excelente recurso para isso.
Quando você não começa com um teste reprovado, você não saberá se ele é bem sucedido porque você tem um erro em seu teste ou porque o código funcional está correto.
Isso lhe dará dores de cabeça quando os testes falharem em um ambiente e forem bem-sucedidos em outro.
Acompanhe-me agora. Você está quase lá.
Aqui estão algumas ferramentas e técnicas para você usar.
Os frameworks de testes unitários fornecem tudo o que você precisa para criar testes unitários
Você pode encontrar um ou mais frameworks de teste unitário para quase todas as linguagens de programação por aí.
Teste unitário runners descobrem os testes em seu código de teste automaticamente, executam todos os testes ou uma seleção específica, e depois reportam os resultados do teste.
Eles vêm como extensões de IDE e como utilitários autônomos de linha de comando. Você pode usar estes últimos em scripts de compilação, de modo que as builds de integração falhem quando um merge quebra o código existente.
Os frameworks de teste unitário têm seus próprios runners, mas você também pode encontrar runners dedicados que podem descobrir e executar testes escritos usando múltiplos frameworks.
Bibliotecas de mock permitem facilmente criar dublês e imitações para testes.
Você os utiliza para fornecer uma classe em teste com instâncias de suas classes colaboradoras. Desta forma, você pode facilmente personalizar o comportamento dos colaboradores de acordo com as necessidades de seu teste.
Inversão de Controle, ou Injeção de Dependência, é um padrão de projeto que você usa para romper laços fortes entre as classes.
Em vez da classe A instanciar uma classe B5, você a fornece com uma instância do ancestral mais abstrato do B5 que dá à A os métodos e propriedades que ela precisa.
Facilita o teste unitário de uma classe com colaboradores, pois é muito mais fácil lhe fornecer imitações.
A vida é boa quando suas equipes Agile estão sincronizadas!
Contate-Nos hoje para uma demonstração personalizada do SwiftEnterprise! Ou inscreva-se para atualizações abaixo.