Teste unitário de código legado – Microsoft Moles (e Pex)

Não é raro acontecer de você precisar criar teste unitário para um dado código legado que, por qualquer motivo, não possui suporte para utilização de desvios (ex: IoC/DI). Nestes casos, ou opta-se por não escrever o teste, ou escreve-se um teste de integração / blackbox englobando rotinas periféricas que aquele fragmento sendo testado faz uso ou, o código legado é alterado para permitir o registro de desvios para teste.

Não acredito que qualquer uma das opções acima seja algo confortável, e acho que a grande maioria das pessoas deve concordar com isto.

Tenha em mente que: Códigos não testados vivem dentro da caixa de Schrödinger. Eles podem estar funcionando ou não. Você só vai ter certeza quando alguém passar a usá-lo.

Mas calma! Este problema não tende mais a afligir programadores .Net. Já faz algum tempo, a Microsoft liberou um casal de ferramentas que visa facilitar a vida do desenvolvedor de testes. Estas ferramentas são as seguintes:

  • Pex: Framework de whitebox testing que permite, de forma automatizada, encontrar fragilidades e problemas em seu código. Possui um grande foco em prover um alto nível de cobertura de código.
  • Moles: Framework, baseado em delegades, para criação de desvios e stubs para plataforma .Net.

Este artigo visa cobrir apenas a utilização de Moles, porém eu gostaria de frisar que o Pex é uma ferramenta tão interessante quanto e vale muito a pena dar uma olhada que seja.*

Código legado

Abaixo, o que considero ser bom exemplo de código que ilustra a situação descrita no primeiro parágrafo:

    public class ClasseLegada
    {
        private Logger _logger = null;
        public ClasseLegada() { this._logger = new Logger(); }
    }

No código acima é fácil notar que não existe uma forma simples de impedir que, durante o teste da classe ClasseLegada, uma instância do objeto de Log seja criado, certo? Não temos nenhuma forma de inserir um Mock ou Stub nesta classe e evitar que isto aconteça.

Agora imagine que nesta classe exista o seguinte método que precisamos testar:

        public void Do()
        {
            Logger.LogInfo("starting 'Do'...") ;
            /// Faz algo que precisamos testar
            Logger.LogInfo("Finished 'Do'...") ;
        }

A situação ficou ainda mais feia! Além da inicialização do objeto no construtor, ainda temos nos preocupar com o fato de que os Logs estão espalhados por todo o método que estamos interessados. Considere que este log está gravando estes dados em disco e, seus testes unitários precisam rodar no servidor de Build onde você, por nenhuma chance no mundo, conseguiria tais privilégios.

Como disse no inicio do post, hoje isto não seria mais um problema. Antes de começarmos, é necessário que você realize o download do Moles diretamente do site da Microsoft Research. Você pode fazer isto facilmente clicando aqui para x86, ou para a x64 aqui.

Após a instalação, uma nova opção aparecerá no menu de referências de seu projeto, possibilitado a criação de moles, como pode ser visto na imagem abaixo:

Por incrível que pareça, isto é boa parte do que você precisa saber sobre a utilização do Moles. É importante salientar que o projeto Moles ainda está sob a responsabilidade da equipe de Research (o que geralmente que dizer que está sendo desenvolvido / aprimorado) e tem alguns bugs e limitações. Porém, posso dizer que já é uma ferramenta que cumpre sua tarefa em boa parte dos desafios que enfrentou comigo.

Ao selecionar “Add Moles Assembly” para a Dll / projeto que você pretende testar (ou mesmo a dependência que você precisa mockar), um arquivo xml com extensão .moles será adicionado ao seu projeto. Você deve então realizar o build do projeto de testes e, com isto, poderá passar a fazer uso dos desvios gerados automáticamente pela ferramenta.

Voltando ao caso da classe “ClasseLegada”, vamos supor que ela exista dentro de um projeto chamado “MinhaLib”. Logo, eu realizei o passo anterior na referência do meu projeto e isto foi o que o framework gerou como resultado final:

Agora, de dentro do meu projeto de testes, eu tenho a possibilidade de usar as classes geradas pelo Moles para realizar desvios no código sem precisar mudar uma linha se quer na implementação original. Para tanto, é preciso entender basicamente que:

  • Para classes, membros públicos e estáticos, existe um padrão que determina que as classes do Moles sejam nomeadas da seguinte forma: M<NomeDaSuaClasse>. O que quer dizer que nossa classe “ClasseLegada” possui agora uma correspondente, com desvios, chamada “MClasseLegada”.
  • Para interfaces e classes abstratas, o moles vai gerar automaticamente Stubs (que podem se transformar em Mocks, a depender do que você escolher fazer com elas). O padrão de nomenclatura neste caso muda para S<NomeDaSuaClasse>.
Fácil né?

Molificando

De posse deste conhecimento, podemos agora escrever os desvios para a “ClasseLegada” de forma a permitir que o teste unitário foque apenas no aspecto específico que queremos testar. Para tanto, podemos por exemplo, fazer uso do seguinte código em nosso teste method:

 MLogger.LogInfoString = _ => { };
 MClasseLegada.Constructor = _ => { };
 new ClasseLegada().Do();

Explicando o código:

  • Na primeira linha, registramos um desvio para todas as chamadas realizadas para o método estático chamado LogInfo, cuja assinatura possua um parâmetro do tipo String. Esta é também uma convenção do Moles para geração de desvios. Membros estáticos e os construtores são adicionados diretamente no nível da classe M*, ao passo que os membros de instância são gerados dentro de uma nested static class chamada AllInstances (geralmente em forma de setters).
  • Na segunda linha, apenas para complementar, apagamos completamente o construtor da classe “ClasseLegada”, uma vez que este processo não nos interessa.
  • A terceira linha é onde realmente realizamos a chamada para o método que queremos testar.
Importante:
  • Em todos os TestMethods que façam uso de moles, é necessário adicionar o seguinte atribuito: [HostType(“Moles”)]
  • Caso você precise criar objetos para classes do .Net Framework (DateTime, Random, etc…), você pode optar por usar a opção “Add Moles Assembly for mscorlib”, disponível no menu exibido quando o botão direito é usado sobre a pasta de references de um dado projeto. O funcionamento é basicamente o mesmo.
Agora, quando seu teste for executado, o moles vai tratar de fazer com que as chamadas realizadas para os fragmentos de código instrumentados sejam executadas dentro dos desvios que registramos. Legal né?

Veja também:

Um comentário

  1. Show de bola!!! Muito bom, prático e muito objetivo o post!! já tinha lido, mas estava postergando para começar a testar melhor e agora estou tendo tempo. Reli e achei muito bom. Parabéns também pelo blog de um modo geral, muitos “artigos”(para não dizer todos) de alta qualidade. !

    Curtir

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

%d blogueiros gostam disto: