Introdução aos testes de unidade com XUnit no .NET

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<Produto> _produtos = new List<Produto>();

// 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<Produto> 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 StartClique 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 ExpertClique aqui

Conclusã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.+