Os testes de unidade são uma técnica fundamental para garantir que o comportamento de pequenas partes de um sistema funcione conforme esperado. No .NET, o XUnit é um framework popular para escrever testes de unidade, devido à sua simplicidade e flexibilidade.
Boas práticas na criação de testes de unidade
Antes de começarmos com a implementação do nosso exemplo, é importante destacar algumas boas práticas para escrever testes de unidade eficazes:
Testes independentes: cada teste deve ser independente dos outros. Isso significa que a ordem de execução dos testes não deve afetar seus resultados.
Nomeação clara: O nome do teste deve descrever claramente o que ele está testando. Isso facilita o entendimento de qual comportamento está sendo validado.
Arrange-Act-Assert (AAA): a estrutura padrão para testes de unidade, que facilita a leitura e compreensão do código de teste:
- Arrange: configura o cenário inicial.
- Act: executa a ação a ser testada.
- Assert: verifica se o resultado é o esperado.
Manter o teste simples e específico: testes devem ser simples, focando em apenas uma funcionalidade ou cenário por vez. Isso facilita a identificação do que falhou em caso de erro.
Evite dependências externas: testes de unidade não devem depender de serviços externos, como bancos de dados ou APIs. Utilize mocking ou stubs para simular dependências.
Teste cenários positivos e negativos: teste tanto os caminhos em que tudo ocorre como esperado (cenários positivos) quanto os erros esperados e exceções (cenários negativos).
Implementando um sistema de estoque
Antes de começarmos a escrever nossos testes, precisamos de um código que será testado. Para isso, vamos desenvolver um sistema de gerenciamento de estoque. O primeiro passo é criar um projeto de console que conterá uma entidade chamada “Produto”.
public class Produto
{
public string Nome { get; set; }
public decimal Preco { get; set; }
public int Quantidade { get; set; }
public decimal ValorTotal { get; set; }
public Produto(string nome, decimal preco, int quantidade)
{
Nome = nome;
Preco = preco;
Quantidade = quantidade;
ValorTotal = quantidade * preco;
}
}
Após isso, vamos criar uma classe chamada “Estoque” em que vamos implementar o gerenciamento:
private readonly List _produtos = new List();
// Adicionar produto ao estoque
public void AdicionarProduto(Produto produto)
{
var produtoExistente = _produtos.FirstOrDefault(p => p.Nome == produto.Nome);
if (produtoExistente != null)
{
produtoExistente.Quantidade += produto.Quantidade;
}
else
{
_produtos.Add(produto);
}
}
// Remover produto do estoque
public void RemoverProduto(string nome, int quantidade)
{
var produto = _produtos.FirstOrDefault(p => p.Nome == nome);
if (produto == null || produto.Quantidade < quantidade)
{
throw new InvalidOperationException("Produto indisponível ou quantidade insuficiente.");
}
produto.Quantidade -= quantidade;
if (produto.Quantidade == 0)
{
_produtos.Remove(produto);
}
}
// Retorna a lista de produtos
public List ListarProdutos()
{
return _produtos;
}
Implementando testes com XUnit
Agora que a lógica do sistema foi implementada, vamos garantir que tudo funcione corretamente através de testes de unidade. O primeiro passo é criar um projeto de teste do tipo XUnit dentro da solução onde está o projeto principal.
O resultado da estrutura da solução deve ser algo semelhante a isso:
Com o projeto de testes criado, clique com o botão direito do mouse sobre o projeto “GestaoEstoqueTest” e a seguir clique em “Adicionar” e selecione a opção “Referência de Projeto…”.
Na janela que abriu, marque o projeto “GestaoEstoque” e clique em OK:
Agora, vamos criar um teste para verificar se a adição de produtos ao estoque está funcionando corretamente, seguindo a abordagem padrão de Arrange, Act e Assert:
[Fact]
public void AdicionarProduto_DeveAdicionarProdutoAoEstoque()
{
// Arrange
var estoque = new Estoque();
var produto = new Produto("Laptop", 5000m, 10);
decimal valorTotalEsperado = 50000m; // Preço esperado após a adição
decimal valorTotalDiferenteDoEsperado = 20500m; // Um preço incorreto
// Act
estoque.AdicionarProduto(produto);
// Assert
var resposta = estoque.ListarProdutos();
Assert.Single(resposta);
Assert.Equal(valorTotalEsperado, resposta[0].ValorTotal);
Assert.NotEqual(valorTotalDiferenteDoEsperado, resposta[0].ValorTotal);
Assert.NotNull(resposta);
Assert.NotEmpty(resposta);
Assert.InRange(resposta[0].Preco, 1000m, 15000m);
}
Arrange:
Nesta fase, preparamos tudo o que é necessário para o teste. Aqui criamos um novo estoque (Estoque), e definimos um produto (Produto) com o nome “Laptop”, preço de 5000m, e quantidade de 10 unidades. Além disso, definimos dois valores esperados para o teste:
valorTotalEsperado: 50000m, que representa o valor total correto do produto após a adição.
valorTotalDiferenteDoEsperado: 20500m, um valor total incorreto que usamos para garantir que o sistema não atribua preços errados.
Act:
No Act, chamamos a ação que queremos testar. Nesse caso, é o método AdicionarProduto(produto), que adiciona o produto “Laptop” ao estoque. Este é o comportamento central que estamos verificando neste teste: a adição correta de produtos ao estoque.
Single
A primeira verificação é se a lista de produtos, após a adição, contém exatamente um item. Isso é importante porque garante que o método “AdicionarProduto” inseriu apenas um produto no estoque e que não houve duplicação ou falha. Se houver mais ou menos de um item na lista, o teste falha.
Equal
Aqui, validamos que o preço do produto adicionado ao estoque corresponde ao valor esperado. Isso confirma que o preço foi corretamente calculado e armazenado no produto. Se o valor armazenado no estoque não corresponder ao esperado, o teste falhará, indicando um possível problema na lógica de adição de produtos.
NotEqual
Este assert serve para garantir que o preço do produto não é igual a um valor incorreto, como o que foi definido em “valorTotalDiferenteDoEsperado”. Ele atua como uma camada extra de segurança, confirmando que o sistema não atribuiu valores errados ao produto, prevenindo erros que poderiam passar despercebidos.
NotNull
Este assert assegura que a lista de produtos retornada pelo método “ListarProdutos” não é nula, ou seja, que o método retornou uma lista válida. Esse assert serve para garantir que o sistema esteja manipulando os dados corretamente e que a coleção de produtos foi inicializada corretamente.
NotEmpty
Além de verificar que a lista não é nula, também garantimos que ela não está vazia. Isso confirma que o produto foi de fato adicionado ao estoque, proporcionando maior confiança de que o processo de adição foi bem-sucedido e que o estoque está funcionando conforme o esperado.
InRange
Por fim, este assert verifica que o preço do produto está dentro de um intervalo aceitável, entre 1000m e 15000m. Isso ajuda a prevenir a inserção de valores absurdamente baixos ou altos, que poderiam ser indicativos de erros no sistema, garantindo que o preço do produto segue dentro de parâmetros razoáveis.
Agora, vamos criar um teste para a remoção de produtos do estoque, simulando uma situação em que o sistema deve lidar com uma falha. Neste caso, tentaremos remover uma quantidade maior de produtos do que a disponível no estoque, o que deve gerar uma exceção. Isso nos permitirá verificar como o sistema se comporta diante de erros e garantir que a lógica de exceção esteja correta.
Acelere a sua carreira conosco!
Se você é Desenvolvedor .NET Júnior e quer acelerar sua carreira até nível Pleno com salário de R$7k+, ou mesmo busca a primeira vaga, conheça a Mentoria .NET Start: Clique aqui Se é Desenvolvedor .NET Pleno ou Sênior e quer virar referência técnica em sua equipe e mercado, com salário de R$10k+, conheça a Mentoria .NET Expert: Clique aquiConclusão
Testes de unidade são uma prática indispensável para garantir a qualidade do software. O XUnit é uma excelente ferramenta para implementá-los de forma eficaz no .NET, oferecendo flexibilidade e integração com outras ferramentas do ecossistema. Adotar a criação de testes desde o início do desenvolvimento aumenta a confiança nas suas entregas e melhora a manutenibilidade do projeto a longo prazo.+