Mocking Test no .NET Core: utilizando o framework NSubstitute
O objetivo deste artigo é descrever o uso de práticas de Mocking Test com o .NET Core, empregando para isto do framework NSubstitute.
Introdução
Nem sempre a implementação de testes de unidade será uma tarefa fácil. Dependências entre as diferentes partes de um software, a inexistência de ambientes específicos para validação e a integração com servidores externos são alguns dos fatores que podem limitar ou, até mesmo, inviabilizar este tipo de prática.
E como superar estas dificuldades? A solução nestes casos envolve a simulação/imitação de comportamentos considerados como impossíveis de se testar. Recursos conhecidos como Mocks simplificam tal processo, fornecendo meios para emular o funcionamento de um objeto e assim permitir sua validação sem grandes esforços de codificação.
Frameworks para a criação de Mock Objects dispensam a implementação de estruturas de código que seriam descartadas posteriormente. Isto acontece através de mecanismos que permitem definir o retorno de métodos, propriedades e até o lançamento de exceções dentro de classes contendo testes de unidade. Duas alternativas gratuitas para mocking na plataforma .NET são os frameworks Moq e NSubstitute.
A finalidade deste artigo é demonstrar a criação de Mock Objects em testes de unidade gerados com o .NET Core, utilizando para tanto o framework NSubstitute.
Criando o projeto a ser testado
Para implementar os projetos apresentados nesta e na próxima seção foram usados os seguintes recursos:
- O Microsoft Visual Studio Community 2017;
- O .NET Core 1.1;
- A versão 2.2.0 do xUnit, framework para implementação de testes de unidade em .NET;
- O NSubstitute 2.0.2.
O exemplo aqui descrito é uma variação de um projeto baseado no framework Moq, o qual foi detalhado em outro artigo que escrevi anteriormente.
Inicialmente será criado um projeto do tipo Class Library (.NET Core) chamado ConsultaCredito, como indicado na imagem a seguir:
A biblioteca ConsultaCredito conterá os recursos empregados na interação com um serviço de consulta a crédito, utilizando para isto o número de CPF de eventuais clientes durante a realização de pesquisas. No próximo diagrama estão as estruturas a serem implementadas nesse projeto:
Na listagem a seguir é possível observar:
- O enumeration StatusConsultaCredito, com os status esperados na interação com o serviço de consulta a crédito;
- A classe Pendencia, com propriedades que identificam a pessoa física e suas eventuais pendências financeiras.
Dentre as situações previstas pelo enumeration StatusConsultaCredito estão:
- O envio de um CPF inválido (valor ParametroEnvioInvalido);
- O lançamento de uma exceção ao invocar o serviço de consulta a crédito (valor ErroComunicacao);
- A inexistência de pendências para uma pessoa física (valor SemPendencias);
- A detecção de pendências financeiras associadas a uma pessoa física (valor Inadimplente).
A interface IServicoConsultaCredito representa a base para a implementação do mecanismo de comunicação com o serviço de consulta a crédito, além do meio através do qual serão criados Mocks para o teste deste processo. Tudo isto acontecerá no método ConsultarPendenciasPorCPF, cujos retornos possíveis são:
- Uma exceção, caso ocorram problemas na comunicação com o serviço;
- Nulo, em se tratando de um CPF inválido;
- Uma coleção com uma ou mais instâncias do tipo Pendencia, se a pessoa física em questão possuir pendências financeiras;
- Uma coleção do tipo Pendencia sem elementos, para pessoas físicas sem qualquer impedimento financeiro.
Já na próxima listagem está a definição da classe AnaliseCredito:
- Este tipo receberá em seu construtor uma instância do tipo IServicoConsultaCredito, a qual será empregada no acesso ao serviço de consulta;
- A comunicação entre a classe AnaliseCredito e tal serviço se dará no método ConsultarSituacaoCPF, que foi preparado para o tratamento dos diferentes retornos ao se invocar a operação ConsultarPendenciasPorCPF de IServicoConsultaCredito.
Implementando os testes
O próximo passo agora consistirá na criação de um projeto de testes chamado ConsultaCredito.Testes. Selecionar para isto o template xUnit Test Project (.NET Core), no qual será feito uso do framework xUnit para a implementação de testes de unidade:
Acrescentar ainda uma referência à biblioteca ConsultaCredito:
E adicionar o package do NSubstitute ao projeto ConsultaCredito.Testes:
Na classe Testes foram implementadas validações para os diferentes casos de teste envolvendo o uso do tipo AnaliseCredito:
- Quatro constantes (CPF_INVALIDO, CPF_ERRO_COMUNICACAO, CPF_SEM_PENDENCIAS e CPF_INADIMPLENTE) foram definidas, com as mesmas correspondendo aos diferentes números de CPF empregados em cada validação prevista pela classe Testes;
- Uma instância do tipo IServicoConsultaCredito (mock) será criada no construtor de Testes, por meio de uma chamada ao método For da classe Substitute (namespace NSubstitute);
- O Mock Object resultante será configurado, através de chamadas aos métodos ConsultarPendenciasPorCPF (que receberá o CPF específico para um caso de teste) e Returns (este último um Extension Method provido pelo framework NSubstitute, no qual será indicado o retorno esperado para cada CPF);
- As operações TestarParametroInvalido, TestarErroComunicacao, TestarCPFSemPendencias e TestarCPFInadimplente foram marcadas com o atributo Fact (namespace Xunit), de forma a permitir que o Visual Studio identifique os métodos/casos de teste a serem executados;
- O método auxiliar ObterStatusAnaliseCredito receberá um CPF e utilizará o Mock Object criado no construtor, a fim de obter o retorno a ser validado por cada um dos 4 casos de teste.
Executando os testes
Para executar o projeto de testes será necessário acessar o menu Tests > Run > All Tests:
Na janela Test Explorer constará o resultado da execução dos diferentes casos de teste:
Conclusão
Frameworks de Mocking Test simulam o comportamento de objetos em diferentes cenários, dispensando os desenvolvedores da necessidade de criar implementações que seriam descartadas/desativadas num ambiente de produção. O NSubstitute exemplifica bem isto, sendo que outro ponto que pesa a seu favor está no fato do mesmo exigir menores esforços de codificação quando comparado ao Moq.