Tenho visto muita gente recentemente se gabando da relação teste-para-código de seus projetos. As pessoas medem a quantidade de linhas de código do sistema em si e a quantidade de linhas de código dos testes automáticos e publicam a relação de uma para a outra, como se isso quisesse dizer algo sobre a qualidade do sistema. Permita-me ser bem claro:
Ninguém dá a mínima para a sua relação teste-para-código.
Nem para sua taxa de cobertura de testes, se é que isso interessa.
A menos que a relação seja zero-para-um, não há absolutamente nada de informativo neste número. A partir do momento em que você está escrevendo algum teste, fazer este número crescer não garante melhoria nenhuma na qualidade do sistema. Talvez você esteja escrevendo testes inúteis para verificar se sua linguagem de programação consegue atribuir um valor a uma variável e recuperá-lo de volta. Talvez você esteja repetindo os mesmos testes o tempo todo. Testar é bom, mas alguns testes são melhores do que outros e este número não é calculado sobre a qualidade dos testes, só a quantidade deles. Não há nenhuma conclusão útil que eu possa tirar do seu número sem dar uma olhada no seu código.
Cobertura de testes é outra métrica bem vazia. Claro que você deve tentar se aproximar tanto quanto possível de 100%, mas a verdade é que chegar a níveis de cobertura acima de 90% não é tão difícil assim. Considere, por exemplo, este trecho de código adaptado do Code Complete do Steve McConnell:
totalWithholdings = 0;
for ( id = 0; id < numEmployees; id++) {
if (m_employee[id].governmentRetirementWithheld < MAX_GOVT_RETIREMENT) {
governmentRetirement = ComputeGovernmentRetirement(m_employee[id]);
}
companyRetirement = 0;
if (m_employee[id].WantsRetirement &&
EligibleForRetirement(m_employee[id])) {
companyRetirement = GetRetirement(m_employee[id]);
}
grossPay = ComputeGrossPay(m_employee[id]);
personalRetirement = 0;
if (EligibibleForPersonalRetirement(m_employee[id])) {
personalRetirement = PersonalRetirementContribution(m_employee[id],
companyRetirement, grossPay);
}
withholding = ComputeWithholding(m_employee[id]);
netPay = grossPay - withholding - companyRetirement -
governmentRetirement - personalRetirement;
PayEmployee(m_employee[id], netPay);
totalWithholdings = totalWithholdings + withholding;
totalGovernmentRetirement = totalGovernmentRetirement +
governmentRetirement;
totalRetirement = totalRetirement + companyRetirement;
}
SavePayRecords(totalWithholdings, totalGovernmentRetirement,
totalRetirement);
É possível obter 100% de cobertura para este trecho completo com apenas um teste: você só precisa garantir que há ao menos um funcionário (para que o loop seja executado) e que as três cláusulas condicionais avaliem para verdadeiro. Obviamente um único teste não é o suficiente para testar todas as nuances deste código e, de fato, McConnel mostra no livro que são necessários pelo menos dezessete testes diferentes. Mesmo assim se tivéssemos escrito aquele único caso de teste, enxergaríamos cobertura total.
Parece que virou moda ultimamente supervalorizar os testes e divulgar estas métricas vazias. No fim das contas elas funcionam como qualquer métrica para software, só dão uma visão bem abstrata do sistema e podem muitas vezes serem enganosas. Não há substituto para uma boa leitura do código.
Tendo dito isso tudo, eu definitivamente não recomendo tentar desenvolver software sem escrever testes automáticos. Já é difícil desenvolver software usando testes, mas é ainda pior sem eles. Só porque você sabe que não é possível provar ausência de defeitos através de testes, não quer dizer que não deva usá-los. Então, por favor, continue escrevendo-os (ou passe a escrever se você não escreve ainda). Só não aja como se eles fossem a última salvação da humanidade. Eles são só a primeira.