Testes Unitários e boas práticas no .NET 5

Victor Fructuoso
4 min readFeb 18, 2021

No último dia 10/02 fiz uma apresentação para o Papo de Dev, evento organizado pela Avanade Brasil sobre Testes Unitários e Boas práticas.

Resolvi escrever sobre o que foi apresentado no evento, espero que curtam!

O que é um teste unitário?

Um teste unitário é um teste que valida componentes ou métodos individualmente, eles só devem testar o código que é responsabilidade do desenvolvedor, dependências externas não devem ser validadas, por dependências externas incluímos também o banco de dados e sistema de arquivos.

Porque escrever testes unitários?

  • Testes unitários levam menos tempo para serem escritos e são mais rápidos de serem executados do que os demais;
  • Segurança nos testes de regressão, é comum que ao realizar um novo desenvolvimento, o desenvolvedor teste apenas a funcionalidade que está sendo criada ou modificada, porém também é comum que ao modificar uma funcionalidade, um comportamento prévio seja impactado, desta forma é possível identificar estes impactos de forma antecipada, evitando assim, que o problema só seja notado no ambiente produtivo.
  • Código mais limpo e menos acoplado, ao escrever um teste unitário o desenvolvedor vai perceber que quanto menos responsabilidade seus métodos tiverem e menor for seu nível de acoplamento, mais fácil será implementar seus testes unitários, desta forma quem está desenvolvendo fica condicionado a respeitar esses princípios, algo que é comumente abandonado em projetos que não tem testes automatizados.

Quais as características de um “bom” teste unitário?

  • Rápidos: Aplicações robustas possuem milhares de testes unitários, e estes por sua vez tendem a executar extremamente rápido (na casa de milissegundos)
  • Isolados: Testes unitários não devem depender de outros testes unitários, devemos poder executar-los paralelamente, isoladamente e principalmente sem uma ordem pré estabelecida.
  • Repetível: Um teste unitário deve sempre gerar o mesmo resultado, não importa quantas vezes ele seja executado.
  • Auto validável: Um teste unitário deve ser validado automaticamente, sem qualquer necessidade de intervenção humana.
  • Tempo de Desenvolvimento: Um teste unitário deve ser extremamente rápido de ser desenvolvido, se um teste demora a ser desenvolvido, provavelmente a aplicação que está sendo testada possui falhas estruturais, por exemplo alto acoplamento, múltiplas responsabilidades, alta complexidade ciclomática.

Boas práticas na hora de escrever seus testes unitários:

Defina o que será testado (Given When Then)

Uma das formas que considero simples de descrever um cenário de testes é o “Given When Then” ou “Dado Quando Então”, por exemplo:

DADO um Desenvolvedor QUANDO o mesmo for persistido no repositório ENTÃO um Id deve ser gerado automaticamente

Organize a Estrutura de Testes (AAA)

A estrutura do próprio teste unitário também é bastante relevante para auxiliar no processo de escrita, leitura e manutenção,desta forma podemos utilizar uma tática conhecida como “Triple A”.

Esta tática consiste em separar o teste unitário em:

  • Arrange: Preparar o teste;
  • Act: Executar a ação;
  • Assert: Validar o resultado.

Preferir uso de constantes ao uso de hardcoded

Assim como em um código convencional, o uso de literais pode prejudicar o processo de manutenção, nos testes unitários devemos evitar seu uso igualmente.

Evitar lógica em testes (if / for / while)

Testes unitários devem ser simples, tão simples que não deve possuir nenhuma lógica, sendo assim, normalmente um teste não possui condições como if, for e while;

Utilizar métodos auxiliares para configuração do teste

Em alguns casos para simplificar o processo de criação dos cenários de testes podemos criar métodos auxiliares, como neste exemplo, estamos criando um método que gera um Developer válido.

Evitar múltiplas validações

Cada teste unitário deve validar uma única regra do código, por exemplo, se criamos um método responsável por receber uma data que deve ser válida e se e futura, é recomendado criar dois cenários de testes separadamente, um para verificar o que acontece com uma data válida no passado e outro com uma data inválida.

Desacoplar código (incluindo referências estáticas)

Outra dica importante é reduzir o acoplamento do código, pois se o seu código for acoplado a outro componente, ambos os componentes precisarão ser testados juntos. Quando o seu código está desacoplado você consegue isolar o seu código de suas dependências e testa-lo isoladamente.

Pacotes utilizados

  • xUnit: utilizado para criar nossas classes de testes;
  • Bogus: utilizado para montar nossas entidades dinamicamente com valores totalmente aleatórios;
  • Microsoft.AspNetCore.Mvc.Testing: utilizado para levantar uma instancia de testes da nossa API (utilizada para realizar testes os testes de integração);
  • Microsoft.EntityFrameworkCore.InMemory: Utilizado para utilizar o Entity Framework em uma versão InMemory permitindo assim, que um novo banco de dados fosse criado dinamicamente a cada execução.

--

--