Me lembro que no início da minha carreira a minha visão sobre testes de software era quase que totalmente a inversa da que eu tenho hoje.
Me lembro de algumas conversas que eu tinha com um analista que trabalhava comigo, eu disse coisas do tipo:
- ... o que menos gosto de fazer é testar...
- ... na verdade eu não deveria testar, fui eu que escrevi o código estou "viciado" nele, outra pessoa é que deveria testar a aplicação...
Basicamente teste de software serve como uma forma de validar que a aplicação está fazendo o que ela deve fazer de maneira aceitável.
O cliente/usuário precisa testar a aplicação antes de aceitá-la senão, ele pode estar comprando gato por lebre e nós desenvolvedores devemos testar a aplicação antes de entregá-la para garantir que não estamos entregando um emissor de null pointers ou coisa que valha para o cliente.
Apesar de as vezes quando eu penso em testes ainda surgir em mim um pequeno sentimento de tristeza o tempo me mostrou que se arriscar a entregar algo sem testar antes é dar tiro no pé.
No princípio era o verbo eu testava executando as aplicações e clicando nas telas, mudando arquivos manualmente ou executando comandos SQLs para preparar o ambiente para a aplicação executar e, baseado nas minhas deduções eu validava se a aplicação estava se comportando como o esperado ou não. Isso me fez gostar menos ainda de testes, o esforço para testar era demasiadamente grande e se a aplicação fosse alterada e eu tivesse de testar novamente, com certeza não testava com a mesma ênfase que a primeira vez o que aumentava em muito o risco de entregar um código cheio de bugs.
Era preciso automatizar os testes e eu aprendi a fazer isso com os xUnit frameworks. Com eles eu podia escrever um código para testar meu código, depois era simplesmente executar o "código de teste" que ele visualmente me mostrava se tudo estava ocorrendo bem ou não. Nada mais de ler arquivos de log, consultar base de dados, ou whatever para saber se tudo estava certo ou não, bastava ficar olhando uma barra, se verde OK se vermelha problemas. Mas novos problemas surgiram eu escrevia o codigo de teste e executava ele naquele dia e tudo funcionava bem, mas no dia seguinte ele nao funcionava mais. :O Alguém havia alterado a base de dados! Um novo problema surgiu, como eu ia controlar o ambiente? Eu precisava garantir que alguns registros estivem na base de dados com um estado inicial controlado. Bom a melhor saida que eu encontrei foi escrever na classe de teste codigo para garantir o estado inicial da base de dados para rodar cada teste. Imagine o inferno em forma de teste, era basicamente isso que eu tinha, um monte de codigo que dava inserts e updates em base de dados e depois executava um monte de deletes. Isso funcionou (quer dizer, me enganou) por um tempo. Mas logo veio outro problema, aplicações que dependem de outras aplicações... E na maioria das vezes eu não podia controlar o comportamento das outras aplicações logo não podia criar um teste para minha aplicação que pudesse ser repetido facilmente... O caos estava novamente presente. Entregar o código sem testar? Jamais! Há uma saída, não é fácil, mas há. Dividir para conquistar Primeiramente eu errava em querer testar tudo de uma vez, desde a posição da combo na tela, logica ao clicar no botao ok, conexão com a base de dados, etc... tudo de uma vez só. Após algumas leituras e muitas conversas com outros profissionais aprendi algumas coisas, uma delas é: Dividir para conquistar. Não adianta escrever um código para testar todos os possíveis problemas de uma aplicação. Isole os tipos de problemas em tipos de testes diferentes. Alguns dos tipos de problemas que uma aplicação pode apresentar são:
- Erros na regra de negócio
- Erro de conexão (base de dados, disco rígido, outras máquinas)
- Não suportar o volume de dados/usuários.
- Demorar demais para executar tarefas.
- Não funcionar integradamente com os outros sistemas.
- Entre outros
Escrever um teste para validar todos esses problemas é inviável, o teste vai demorar demais para executar o que vai dificultar sua execução, e tudo que dificulta a o dia-a-dia de um desenvolvedor costuma ser cortado. Mas não escrever nenhum teste também é terrível, então comece pelo simples, divida seus testes.
Testes unitários
Testam apenas o código da aplicação, não é preciso conectar na base de dados, acessar arquivos, levantar servidor ou qualquer coisa do tipo. Testam cada parte (classe em liguagens OO) da aplicação individualmente. Com eles podemos testar todas as regras de negócio da aplicação (Isto se não houver o cenário de regra de negócio na base de dados, dai são outros 500). Tendo os testes unitários já garantimos que grande parte da aplicação tenha testes automatizados. Além disso o código que tem teste teoricamente está fazendo o que deveria fazer.
Teste de integração
Testam a comunicação da aplicação com outros ambientes como base de dados, arquivos no disco, outras máquinas, etc... Esses testes são muito importantes, comigo geralmente eles evidencia erros em comandos SQLs ou má formatação de arquivos textos, etc... Mas esses testes dependem de configurar o ambiente antes o que os torna pesados.
Teste de carga
Como o próprio nome diz, testam como a aplicação se comporta com grande volume de dados/usuários.
Teste de performance
Testam se a aplicação está executando seus processos dentro de um SLA aceitável.
Teste de aceitação
Testam a aplicação do pontos de vista do usuário, simulando a interação do mesmo com a aplicação.
Nos próximos posts falarei sobre cada um dos itens acima e tentarei mostrar pequenas receitas para cada um deles.
