Cobertura de Código - Só para testes unitários?

Antes de discutir cobertura de código, uma rápida recapitulação:

  • Cobertura de código: Uma métrica que indica a efetividade dos testes feitos em uma aplicação. Expressa em termos de porcentagem do código-fonte da aplicação, mostra extaamente o quanto da aplicação foi testada durante uma dada bateria de testes;
  • Testes Unitários: Pequenas rotinas (tipicamente escritas na mesma linguagem de programação do sistema que será testado) que descrevem regras de negócios e outros comportamentos conhecidos e esperados do sistema. Os testes unitários invocam rotinas e outras pequenas porções de código do sistema-alvo, suprindo parâmetros com valores pré-definidos e analisando o comportamento resultante, que deve estar de acordo com a regra de negócio ou comportamento esperado em questão.

Para que os testes unitários sejam efetivos, eles devem cobrir o maior número possível de “caminhos” no código da aplicação. A cobertura de código visa a responder justamente o quanto desses caminhos foi realmente testado. Veja um exemplo (totalmente fictício, hein?!) que ilustra tudo isso:

**Regra de Negócio:** _Para o cálculo do salário líquido de um funcionário, dados:_ 1. _O valor do salário bruto; _ 2. _Um "flag" indicando a adesão ao plano de saúde; _ 3. _Um "flag" indicando a adesão ao vale-refeição;_ _Deve ser efetuado o seguinte cálculo:_ 1. _Descontar o imposto de renda de acordo com com as seguintes faixas: _ 1. _R$0 - R$1000: Isento; _ 2. _R$1001 - R$2000: 5%;_ 3. _R$2001 - R$3000: 15%;_ 4. _R$3001 em diante: 25._ 2. _Descontar 10% de INSS;_ 3. _10% do salário bruto, caso tenha aderido ao plano de saúde; _ 4. _5% do salário bruto, caso tenha aderido ao vale-refeição._ **Código-fonte da aplicação:** 1: public class Empregado
</br> 2: {
</br> 3: public static double CalcularSalario(double salarioBruto, bool temPlanoSaude, bool temValeRefeicao)
</br> 4: {
</br> 5: var salarioLiquido = salarioBruto;
</br> 6:
</br> 7: // Cálculo do imposto de renda
</br>
8:
</br> 9: if (salarioBruto > 1000 && salarioLiquido <= 2000)
</br> 10: {
</br> 11: salarioLiquido -= (salarioBruto * .05);
</br> 12: }
</br> 13: else if (salarioBruto > 2000 && salarioBruto <= 3000)
</br> 14: {
</br> 15: salarioLiquido -= (salarioBruto * .15);
</br> 16: }
</br> 17: else if (salarioBruto > 3000)
</br> 18: {
</br> 19: salarioLiquido -= (salarioBruto * .25);
</br> 20: }
</br> 21:
</br> 22: // Cálculo do plano de saúde
</br> 23:
</br> 24: if (temPlanoSaude)
</br> 25: {
</br> 26: salarioLiquido -= (salarioBruto * .10);
</br> 27: }
</br> 28:
</br> 29: // Cálculo do vale-refeição
</br> 30:
</br> 31: if (temValeRefeicao)
</br> 32: {
</br> 33: salarioLiquido -= (salarioBruto * .05);
</br> 34: }
</br> 35:
</br> 36: return salarioLiquido;
</br> 37: }
</br> 38: } **Teste Unitário:** public void CalcularSalarioTest() { // "Valores conhecidos". São arbitrários; posso usar qualquer valor double salarioBruto = 1500; bool temPlanoSaude = false; bool temValeRefeicao = true; // "Valor esperado". Deve estar de acordo com a regra de negócio e com // os valores dados acima double expected = 1350; // "Valor real". É o valor que será retornado pela minha rotina de cálculo; // se tudo der certo, deve ser igual ao "valor esperado" double actual; // Executa a rotina passando os valores acima como parâmetros actual = Empregado.CalcularSalario(salarioBruto, temPlanoSaude, temValeRefeicao); // Confere se os resultados ("valor esperado", "valor real") são iguais; se forem, // o teste é considerado bem-sucedido Assert.AreEqual(expected, actual); }

Compare a regra de negócio, o código-fonte da aplicação e o teste unitário:

  • A rotina de cálculo está atendendo a todas as premissas da regra de negócio? No exemplo acima, sim.
  • O teste unitário está testando todas as variações possíveis da rotina de cálculo? No exemplo acima, não está:
    • Não foram feitos testes para todas as faixas de salários (para garantir que o IRPF está sendo calculado corretamente);
    • Não foram testadas outras variações para “plano de saúde” e “vale-refeição”.

É justamente nesse momento que entra a cobertura de código. Ela nos ajuda a identificar quais pontos da rotina não foram testados:

image

Viu? A cobertura de código é quase indispensável quando estamos trabalhando com testes unitários. Ela nos dá a medida da eficácia do teste que escrevemos e acabamos de executar. Daí vem uma dúvida bastante comum quando discutimos cobertura de código e testes unitários no Team System. Tipicamente esses dois recursos são mostrados em conjunto - e por isso há quem acredite que são parte da mesma funcionalidade, e que portanto cobertura de código significa “porcentagem do código testado pelos testes unitários”, quando na verdade significa “porcentagem do código testado”!

Cobertura de Código e outros testes

Veja como a cobertura de código se comporta em conjunto com alguns dos outros testes disponíveis no Team System. Para isso, crie uma solução com dois projetos: uma Web Application e um Test Project. Não se esqueça de ativar a cobertura de código para seu projeto Web Application (Test Edit Test Run Configurations Local Test Run):

image

Testes Manuais

Faça uma experiência: adicione um teste manual e execute-o. Enquanto ele estiver em execução, abra o seu web site (que deve ter sido carregado automaticamente pelo Visual Studio). Navegue à vontade pelas páginas de teste que você deve ter criado (espero que sim!). Depois, no Visual Studio, marque o teste manual como concluído:

image

Ao clicar numa das opções acima (e em seguida em Apply) você terá concluído o teste. Inspecione agora a janela de resultado da cobertura de código (Test Windows Code Coverage Results). Veja como a cobertura de código foi corretamente preenchida de acordo com as páginas que você navegou:

image

Testes Web e Testes de Carga

Testes Web (Web Test) e Testes de Carga (Load Test) também alimentam a cobertura de código. Faça seus testes e confira!

Outros tipos de testes

Veja como alguns dos outros tipos de teste disponíveis no Team System se comportam quanto à cobertura de código:

  • Testes Unitários de Banco de Dados: Esses testes nada mais são que o teste unitário convencional com algumas facilidades para a execução de T-SQL. Na verdade, por baixo dos panos eles são .NET puro. Assim, se por acaso invocarem trechos da sua aplicação então a cobertura de código refletirá tal fato;
  • Testes Ordenados: Como estes são nada mais que agrupadores de outros testes, estarão sujeitos às regras aplicáveis a cada um dos “sub-testes” que os compõem;
  • Testes Genéricos: Esses testes existem para que possamos testar componentes não- .NET ou que sejam externos à nossa aplicação. Assim, não faz sentido esperar que esses testes “sensibilizem” a cobertura de código.

Conclusão

A cobertura de código é um recurso muito importante da Test Edition do Visual Studio Team System, e está disponível para a maioria dos tipos de testes - não só para testes unitários. Use e abuse da cobertura de código!



11/06/2008 | Por Igor Abade V. Leite | Em Técnico | Tempo de leitura: 6 mins.

Postagens relacionadas