Tag Archives: teste de software

O que são testes de mutantes?

Como garantir que nossa bateria de testes está boa? Essa é uma pergunta importante, afinal de nada adianta termos testes automatizados se eles não forem bons o suficiente. Uma maneira bem conhecida é calcular a cobertura do código. Ferramentas como o Cobertura dizem pra você quais linhas do seu código de produção não são executadas pela sua bateria de teste. Assim, você pode facilmente ver qual if você esqueceu de testar, e escrever um teste pra ele.

Mas essa não é a única maneira. Uma delas, bastante interessante e muito popular na academia (mas não tão popular na indústria) é o teste de mutante. A ideia é genial. Imagine que tenhamos testes verdes para o seguinte trecho de código:

Veja que o if verifica se a é MAIOR que 10. O que deveria acontecer com os testes se mudássemos o sinal de MAIOR para MENOR? Nossa bateria de testes deveria quebrar, certo? Pois é exatamente isso que testes de mutantes fazem. Um mutante é simplesmente uma versão alterada do seu código de produção. Alterar o sinal de maior para menor é uma maneira de gerar um mutante. Mudar o sinal de + pra -, * por /, e assim por diante. E a ideia é que, se rodarmos os testes trocando a classe por um mutante, algum teste deve falhar. Afinal, o mutante faz coisa errada!

Ferramentas como o Pitest pegam seu código de produção, geram diferentes mutantes, e executam a bateria de testes. Se algum teste falha, dizemos então que o mutante foi morto. Agora, se algum mutante sobrevive, então você precisa melhorar seus testes.

Para exemplificar, veja meu repositório pitest-fizzbuzz. Ele tem uma simples implementação do jogo Fizz Buzz. É o mesmo exemplo que usei na pequena série de começando com testes, aqui no meu blog. Você entenderá o jogo pelo código:

Veja os testes dele (que fizemos juntos no blog):

A bateria de testes parece bem completa. Veja o resultado do relatório emitido pelo Pitest. Tudo verde, ou seja todos os mutantes foram mortos (ahá, eu mandei bem quando escrevi os testes! 🙂

pitest-sucesso

Agora, vamos supor que tivéssemos esquecido o teste deveRetornarFizzBuzzQuandoMultiploDe5e7(). Olha só o relatório. Ele nos mostra bem a linha do código de produção onde ele fez a mutação (nesse caso, as mutações), a descrição, e mostra que o mutante sobreviveu. Nesse caso, ele mudou o % por *, e nenhum teste pegou.

pitest-erro

Até aí, parece que o cobertura também pegaria isso. Afinal, essa linha não foi coberta. No entanto, testes de mutantes vão além. Mas imagine uma nova regra no jogo: Números maiores ou iguais a 500 imprimem “big”. Implementamos:

E adicionamos o teste:

Agora, a cobertura desse código é 100%.

pitest-cobertura

No entanto, veja o teste de mutante. Ele pegou! Afinal, não testamos o caso do igual no maior-ou-igual. Uma mutação ali (trocar por exemplo para só maior), e o mutante sobrevive!

pitest-erro3

Testes de mutantes são bastante interessantes, e ainda muito desafiadores para a academia. Afinal, em um sistema grande, o número de mutantes pode ser muito grande. Como encontrar os melhores mutantes? Como remover mutantes redundantes? No site do pitest, você pode ler um pouco mais sobre mutantes e ver as operações de mutação que ele implementa.

A ferramenta tem plugin para o Maven, então é fácil de executar. No repositório de exemplo, o pom.xml está configurado.

Eu preciso de 100% de cobertura de testes?

Mito: não ter 100% de cobertura é a mesma coisa que nada!

Muitas pessoas discutem a necessidade de ter 100% de cobertura em um código. Mas não vejo problemas em códigos que não tenham 100% de cobertura de testes de unidade.

Acredito que isso deve ser uma meta da equipe, buscar sempre a maior cobertura possível; mas essa é uma meta que você provavelmente não vai alcançar; alguns trechos de código simplesmente não valem a pena serem testados de maneira isolada!

Explico: Veja essa classe do Restfulie.NET, por exemplo, chamada AspNetMvcUrlGenerator: ela serve para gerar URLs para Actions em Controllers, utilizando as rotas pré-definidas. Repare que ela faz uso intenso das APIs do Asp.Net MVC, utilizando inclusive alguns métodos estáticos (que sabemos que é difícil de testar) como no HttpContext.

public class AspNetMvcUrlGenerator : IUrlGenerator
    {
        public string For(string controller, string action, IDictionary values)
        {
            var httpContextWrapper = new HttpContextWrapper(HttpContext.Current);
            var urlHelper = new UrlHelper(new RequestContext(httpContextWrapper, 
              RouteTable.Routes.GetRouteData(httpContextWrapper)));

            return FullApplicationPath(httpContextWrapper.Request) + 
              urlHelper.Action(action, controller, new RouteValueDictionary(values));
        }

        private string FullApplicationPath(HttpRequestBase request)
        {
            var url = request.Url.AbsoluteUri.Replace(request.Url.AbsolutePath, 
               string.Empty) + request.ApplicationPath;
            return url.EndsWith("/") ? url.Substring(0, url.Length - 1) : url;
        }
    }

Eu até poderia ter feito alguma mágica e escrito um teste de unidade para esse código. Mas para quê? Apenas para aumentar o número? Não faz sentido! Esse trecho de código precisa de um teste de integração, e não de um teste de unidade!

Um outro exemplo é o teste de propriedades (Properties do C#). Preciso realmente deles? A própria linguagem implementou isso pra mim. O mesmo acontece no caso dos getters/setters do Java, onde o programador geralmente usa o Eclipse para gerá-los.

Você precisa cobrir seu código de testes, mas você pode usar testes de diferentes níveis para isso (testes de unidade, de integração, de sistema, etc)! Escrever testes de unidade inúteis, apenas para chegar nos 100% de cobertura, é desperdício.

Quando devo apagar testes?

O código dos testes é tão importante quanto código de produção. E provavelmente você já ouviu aquela famosa frase: “melhor do que escrever código, é apagar código!”. Quando é então que eu apago código de teste?

A primeira e mais óbvia resposta é: quando o teste deixar de fazer sentido! Se a funcionalidade foi removida, você deve atualizar sua bateria de testes e apagar todos os testes relacionados à ela. Bateria de testes desatualizada não serve pra nada! Se a funcionalidade evoluir, você deve evoluir seus testes juntos.

Até aí nada de novidade… Mas vamos lá.

A segunda resposta é: quando você tem testes repetidos! Em algumas situações, quando estamos em dúvida sobre como implementar determinada funcionalidade, optamos por escrever testes parecidos para, de alguma forma, triangularizar até chegar na implementação correta.

Voltando ao velho exemplo da calculadora. Suponha que implementar um algoritmo de soma fosse algo complicado. Você começou com testes simples, como (1+1), depois (1+2), depois (2+2). Nesse momento você encontrou uma maneira de resolver o problema para quaquer (m+n). Seus testes de unidade ficam parecidos com esses:

Esses testes, muito úteis durante o tempo de desenvolvimento do algoritmo, agora se tornaram repetidos. Você, portanto, deve apagá-los! Eles, além de serem inúteis, ainda dificultam o trabalho do desenvolvedor. Se um dia o método testado mudar, você terá que mudar em 10, 20 testes diferentes (mas que testam a mesma coisa!). Lembre-se do acoplamento entre seu código de teste e seu código de produção (sim, ele existe!).

Mas poxa, um testezinho só não é pouco? Não! Você precisa de apenas um teste para garantir a funcionalidade. Não adianta testar a mesma coisa duas vezes.

Você deve ter apenas um teste para cada conjunto de estados válidos e inválidos para uma condição de entrada. A ideia é que todos os elementos de uma classe se comportem de maneira similar. A esses conjuntos damos o nome de classes de equivalência. Escrever apenas um teste por classe de equivalência é uma prática muito comum em testes de caixa preta e é conhecida como particionamento em classes de equivalência. Apesar disso, acredito que ela faça sentido também para testes de caixa branca, como os testes de unidade.

No nosso exemplo da calculadora, poderíamos ter testes para, por exemplo:

  • soma de dois números positivos;
  • soma de um número positivo com outro negativo;
  • soma de um número negativo com outro positivo;
  • soma de dois números negativos;
  • soma com um dos elementos sendo zero;

Obviamente, encontrar todas as classes de equivalência não é um trabalho fácil, e por isso temos a gigante área de testes de software. Mas não é repetindo testes que você garante que seu código funciona.

Referências

Maldonado, Jino, Delamaro. Introdução ao teste de software. Editora Campus, 2007.

Eu faço TDD. Preciso testar?

Claro que sim.

TDD é uma atividade de design. O teste de unidade que você escreve serve basicamente para definir suas expectativas em relação ao código que você vai escrever. E, ao fazer isso, você pensa não só no nível da implementação do algoritmo, mas em um nível um pouco mais alto: no nível de design. TDD permite que você brinque e experimente diferentes possíveis designs, dando feedback rápido sobre o resultado obtido, até que você encontre o design ideal para aquela situação.

Ou seja, quando você faz TDD, você pensa exclusivamente em design e não em testes. É uma prática de suporte para design de software.

Mas… É claro que você precisa testar! Você não apaga os testes que você produziu usando TDD, óbvio. Mas muito provavelmente eles não testam todas as possibilidades possíveis. E é aí que outras técnicas entram em cena, e você pode encontrar muita informação sobre elas em [1][2].

Resumindo, use TDD quando você precisa trabalhar no design de determinada classe ou módulo. Quando você estiver satisfeito com design, é hora de testar! Aí você pode fazer test-first, test-last, ou que você preferir, mas teste de verdade!

[1] The Art of Software Testing – Myers
[2] Introdução ao Teste de Software – Maldonado, Delamaro, Jino

Como gerenciar a dependência entre entidades e repositórios?

Engraçado que esse é um assunto onde há sempre mais um “ponto” a acrescentar. Motivados pelo excelente post da Caelum e discussão do GUJ (onde ambos não são tão recentes assim), eu e o Eduardo Amuri discutimos mais alguns pontos… Alguns deles podem já ter sido abordados nos links anteriores, mas vale a revisão.

Eu ia pular a questão inicial, mas vou revisar para que facilite o entendimento do resto do post para quem não leu os links acima. Uma entidade pode conhecer/utilizar um repositório? Acho que a resposta é óbvia: Sim. Repositórios são conceitos de negócios, fazem parte do domínio, e por isso não há nada de errado em uma entidade conhecer um repositório. Não vou entrar no mérito de como o repositório deve ser implementado, não é o meu interesse no momento. Mas vamos lá… Ok, podemos ter entidades que dependem de um repositório, e o problema é: como gerenciar essa dependência?

A seguir, discutimos algumas abordagens para resolver esse problema e chegamos em algumas conclusões, das quais gostaria de compartilhar.

Sugestão I: Receber o repositório pelo construtor
A entidade deve possuir um construtor que receba a dependência.

Vantagens:

  • A dependência fica explícita, ou seja, não há como criar uma entidade sem entregar a dependência já resolvida.

Problemas:

  • Torna o código menos legível, afinal sempre que for instanciar uma entidade, deve-se injetar seu repositório.
    Ex: Pessoa mauricio = new Pessoa ( repositorioDePessoas );
  • Imagine uma entidade que depende de mais de um repositório (por quê não?). Imagine passar os diversos repositórios no construtor. Menos legível ainda…
    Ex: Pessoa mauricio = new Pessoa ( repositorioDePessoas, repositorioDeXPTO, repositorioDeXYZ );
  • Isso fica pior ainda quando definimos um construtor que receba algum atributo, por exemplo.
  • Ex: Pessoa mauricio = new Pessoa ( “mauricio”, “aniche”, repositorioDePessoas, repositorioDeXPTO );

Testabilidade:

  • É fácil injetar um mock no lugar da dependência, para facilitar o teste unitário da entidade.

Sugestão II: Definir um setter para a dependência
A entidade deve ter um método setDependencia() que receba a dependência.

Vantagens:

  • Elimina o problema de resolver a dependência no momento da criação do objeto.

Problemas:

  • Você precisa setar a dependência sempre que for utilizar, e isso pode ser um problema. Você precisará fazer todo o tempo algum tipo de validação no código da entidade para saber se a dependência já foi injetada ou não.
    Ex: public List<XPTO> pegaXpto(int abc) { if(repositorio!=null) repositorio.pegaX(abc); }

Testabilidade:

  • Também é fácil injetar um mock no lugar da dependência.

Sugestão III: Utilizar o próprio repositório para injetar repositórios nas entidades
Conforme sugerido pelo Paulo Silveira e pelo Fabio Kung no post, sempre que um objeto for recuperado pelo seu repositório, o mesmo deve se “auto-injetar” antes de devolver o objeto.

Vantagens:

  • A complexidade do gerenciamento desse dependência fica escondida no código do repositório.
  • O código a ser implementado, de maneira geral, é simples.

Problemas:

  • Apesar de ser simples, deve ser replicado em todos os métodos do repositório. Se o método do repositório retornar uma lista, por exemplo, deve-se repetir o processo em todos os elementos dessa lista.
  • Apenas as entidades que são recuperadas pelo repositório recebem a dependência. Entidades criadas em qualquer outro ponto do projeto não recebem essa dependência de forma automática. Isso pode causar o problema levantado na sugestão II.
  • Caso a entidade mude e venha a depender de um outro repositório, você terá muito código para alterar.

Testabilidade:

  • Da mesma maneira que o repositório injeta a dependência (através de um setter), pode-se injetar um mock.

Sugestão IV: Injetar o repositório por AOP
Uma idéia bastante interessante seria injetar a dependência por AOP. Dessa maneira não importaria como o bean é instanciado, seja ele feito pelo JPA, Hibernate, etc, ou pelo programador. Essa foi uma sugestão levantada pelo Alessandro Lazarotti, nesse post do GUJ.

Vantagens:

  • O código fica bastante claro.

Problemas:

  • A implementação de um aspecto geralmente não é trivial.

Testabilidade:

  • Dependendo do modo que o aspecto for implementado, talvez não seja tão trivial injetar a dependência. Para facilitar, pode-se criar um setter exclusivamente para isso, mas não é muito elegante.

Sugestão V: Resolver a dependência dentro da própria entidade
A idéia seria a própria entidade resolver a sua dependência. No caso, ela mesma invocaria o framework de DI, e recuperaria a instância da dependência. Pode-se até pensar em uma Factory para que a entidade não dependesse diretamente de uma implementação de framework de DI.

Vantagens:

  • Solução bastante simples.
  • Acaba com o problema da necessidade da entidade validar se a dependência foi injetada ou não, já que ela mesma vai resolver.

Problemas:

  • Não é muito elegante fazer com que a própria classe resolva suas dependências.

Testabilidade:

  • Como é a própria classe que resolve a dependência, você precisará de um setter para conseguir injetar um mock. Como dito acima, não é muito legal.

(*) Além do que foi citado acima, existe um outro problema, ortogonal ao das dependências, e diz respeito diretamente à implementação desse projeto em particular. Por estarmos discutindo uma aplicação web, o contexto do Spring está guardado dentro do contexto web. Para que um POJO qualquer recupere esse contexto, ele precisaria conhecer o servletContext. Ainda não encontrei uma maneira muito clara para resolver isso, mas minha sugestão é criar uma classe singleton que conteria um atributo com uma instância de contexto do Spring, e essa instância seria injetada por um filter ou um listener qualquer, que subiria logo após ao listener do Spring. A partir daí, todas as classes de domínio fariam acesso à esse singleton para capturar o contexto.

Portanto, é possível gerenciar essa dependência de diversas formas, todas elas com suas vantagens e desvantagens. E você, como faz?

.NET Architects Day 2009 foi um sucesso!

Participei nesse último sábado (27/06) no .NET Architects Day, evento organizado pelo grupo de arquitetura focado em .NET, o .NET Architects, liderado pelo Giovanni Bassi. O evento foi muito além do que eu esperava! A organização foi perfeita, a localização era perto do metrô, o auditório era muito bom, e o coffee break estava delicioso! 🙂

E o conteúdo? Devo dizer também que todas as palestras foram muito boas! O nível foi altíssimo e a plateia fez perguntas muito inteligentes em todas elas. Parabéns ao Giovanni Bassi, Juliano Oliveira, Vitor Cavalcante e Leandro Daniel pelas palestras.

A minha palestra sobre testes automatizados também foi legal, acho que consegui cumprir meu objetivo que era a de motivar todos a testar, e como diria Bryan Lyles, “test all the fucking time”! Obtive um feedback muito bom dos participantes, e fico feliz com isso, já que dessa maneira posso melhorar ainda mais a palestra para as próximas oportunidades.

Sobre os vídeos, eles serão publicados em breve. Os slides você pode ver no slideshare aqui.

Enfim, obrigado a todos os organizadores e a todos que foram ao evento. Ano que vem vai ser melhor ainda! Que venha o DNAD 2010!

.NET Architects Day ’09

A comunidade .NET Architects está organizando um evento de arquitetura de software em .NET, no dia 27/06 na Unip Tatuapé.

As palestras estão muito boas, serão abordados DDD (Domain-Driven Design), Injeção de Dependência, ASP.NET MVC, ORMs (com NHibernate) e Testes Automatizados. Eu serei palestrante e falarei sobre testes automatizados!

O evento custa apenas R$ 50,00! Corra, as inscrições estão acabando! 🙂

Mais informações em http://www.dotnetarchitects.net/dnad2009