ASP.NET Core  -  Realizando Testes Unitários - II


Neste artigo veremos como realizar testes unitáriois na ASP .NET Core usando o xUnit.

Continuando a primeira parte do artigo vamos criar o projeto de testes.

Na plataforma .NET temos 3 opções de frameworks disponíveis para realizar testes de unidade :

  1. MSTest - É o mais simples entre os três, sua utilização é bem fácil, bastando adicionar o atributo [TestClass] e [TestMethod] respectivamente nas classes e métodos para iniciar a execução dos testes, outro ponto positivo é que o Visual Studio já vem com um UI Panel para exibir os seus testes. Sem dúvida um dos seus pontos fortes é a integração com o VS e a facilidade de uso;
     
  2. NUnit - É o mais popular, sendo muito usado desde 2006, assim tornou-se referência para testes de unidades, e sem dúvida é uma excelente solução para testes. A sua instalação no projeto é via NuGet package, bastando apenas procurar por NUnit no gerenciador de pacotes. Sua implementação é bastante similar ao MSTest, sendo que [TestClass] passa a ser [TestFixture] e [TestMethod] passa a ser apenas [Test] e testes parametrizados podem ser realizados apenas adicionando [TesteCase(“seu parametro”)];
     
  3. xUnit - Nasceu a partir do NUnit. Os antigos criadores do NUnit se uniram  com o objetivo de criar um framework de testes mais flexível e abrangente. Com isso encontramos muitas semelhanças entre ambos, apesar disso também existem diversas diferenças, como por exemplo, os nomes dos atributos; no XUnit não existe a necessidade de tornar uma classe testável como no MSTest[TestClass] e NUnit[TestFixture], neste basta decorar os métodos com os atributos [Facts] e [Theory], para testar métodos sem e com parâmetros respectivamente;

Criando o projeto de testes

Antes de criarmos o projeto de testes vamos apresentar o xUnit.net que será a ferramenta que iremos usar para realizar os testes de unidade em nosso projeto.

O xUnit.net é uma ferramenta de teste de unidade gratuita, de código aberto e focada na comunidade para o .NET Framework.

Ele foi escrito pelo mesmo autor do NUnit v2, e é a última tecnologia para testes unitários para C#, F#, VB.NET e outras linguagens .NET. Ele também trabalha com ReSharper, CodeRush, TestDriven.NET e Xamarin,  faz parte da Fundação .NET e opera sob seu código de conduta. Está licenciado sob o Apache 2 (uma licença aprovada pelo OSI).

Vamos abrir a solução APICompras criada na primeira parte do artigo e vamos incluir um novo projeto de testes de unidade.

No menu File selecione Add -> New Project;

A seguir selecione :

Escolha o template xUnit Test Project (.NET Core) e clique em Next :

A seguir informe o nome APIComprasTest e clique em Create.

Teremos o projeto incluído na solução conforme mostra a figura abaixo:

Vamos excluir a classe UnitTest1.cs incluida no projeto e a seguir incluir uma referência ao projeto APICompras.

Clique com o botão direito do mouse sobre o projeto APIComprasTest e a seguir clique em Add Reference;

Na janela Reference Manager marque o projeto ApiCompras e clique em OK:

.

Ao final teremos a nossa solução com a seguinte estrutura contendo os dois projetos:

Configurando o projeto de testes

Vamos agora configurar o projeto de testes criando uma implementação fake do serviço ICompraService para depois injetar o serviço no controlador.

Crie no projeto de testes a classe CompraServiceFake que implementa ICompraService com o código abaixo:

using APICompras.Models;
using APICompras.Services;
using System;
using System.Collections.Generic;
using System.Linq;
namespace APIComprasTest
{
    public class CompraServiceFake : ICompraService
    {
        private readonly List<CompraItem> _compra;
        public CompraServiceFake()
        {
            _compra = new List<CompraItem>()
            {
                new CompraItem() { Id = 1, Nome = "Tablet SamSung 7",
                                   Fabricante ="SamSung", Preco = 765.00M },
                new CompraItem() { Id = 2, Nome = "IPad 7",
                                   Fabricante ="Apple", Preco = 644.00M },
                new CompraItem() { Id = 3, Nome = "Notebook Lenovo 13",
                                   Fabricante ="Lenovo", Preco = 987.00M },
                new CompraItem() { Id = 4, Nome = "Monitor LG 23",
                                   Fabricante ="LG", Preco = 879.00M },
                new CompraItem() { Id = 5, Nome = "HD SSD Asus 1T",
                                   Fabricante ="Assus", Preco = 612.00M }
            };
        }
        public IEnumerable<CompraItem> GetAllItems()
        {
            return _compra;
        }
        public CompraItem Add(CompraItem novoItem)
        {
            novoItem.Id = GeraId();
            _compra.Add(novoItem);
            return novoItem;
        }
        public CompraItem GetById(int id)
        {
            return _compra.Where(a => a.Id == id)
                .FirstOrDefault();
        }
        public void Remove(int id)
        {
            var item = _compra.First(a => a.Id == id);
            _compra.Remove(item);
        }
        static int GeraId()
        {
            Random random = new Random();
            return random.Next(1, 100);
        }
    }
}

Nesta implementação definimos um código com dados em memória para representar o nosso serviço com dados fictícios.

Agora vamos criar os testes de unidades no controlador ComprasControllerTest, mas antes vamos definir alguns conceitos usados.

xUnit - Conceitos básicos

No xUnit precisamos decorar os métodos de teste com o atributo [Fact], que é usado pelo xUnit para marcar os métodos de testes. Além dos métodos de testes, também podemos ter vários métodos auxiliares na classe de teste.

Com o XUnit para tornar um método comum em método de testes basta adicionar [Fact] ou [Theory] acima de sua assinatura, os atributos diferem apenas no seguinte, para testes sem parâmetros deve-se usar [Fact], para testes como parâmetros utiliza-se o [Theory].

Ao escrever testes unitários, em geral seguimos o princípio AAA :  Act, Arrange e Assert (Organizar, Agir e Assertir):

Arrange - È aqui que você normalmente prepara tudo para o teste, em outras palavras, prepara a cena para testar (criar os objetos e configurá-los conforme necessário)

Act - È onde o método que estamos testando é executado;

Assert - Esta é a parte final do teste em que comparamos o que esperamos que aconteça com o resultado real da execução do método de teste;

Os nomes dos métodos de teste devem ser tão descritivos quanto possível. Na maioria dos casos, é possível nomear o método para que nem seja necessário ler o código real para entender o que está sendo testado.

No exemplo, usamos a seguinte convenção de nomenclatura :

1 - A primeira parte do nome representa o nome do método que está sendo testado;
2-  A segunda parte do onome nos informa mais sobre o cenário de teste;
3-  A  última parte do nome é o resultado esperado;

Exemplo : Get_WhenCalled_ReturnsOKResult  (estou usando a língua inglesa)

Geralmente, a lógica dentro de nossos controladores deve ser mínima e não tão focada na lógica ou infraestrutura de negócios (acesso a dados etc.).

Queremos testar a lógica do controlador e não as estruturas que estamos usando.

Testando os métodos Action

O primeiro método que vamos testar é o método Get e vamos querer verificar o seguinte:

1 - Se o método retorna o OkObjectResult, que representa o código de resposta HTTP 200;

2 - Se o objeto retornado contém nossa lista CompraItem e todos os nossos itens;

Vamos agora criar a classe ComprasControllerTest no projeto e incluir o código abaixo:

using APICompras.Controllers;
using APICompras.Models;
using APICompras.Services;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using Xunit;
namespace APIComprasTest
{
    public class ComprasControllerTest
    {
        ComprasController _controller;
        ICompraService _service;
        public ComprasControllerTest()
        {
            _service = new CompraServiceFake();
            _controller = new ComprasController(_service);
        }
        [Fact]
        public void Get_WhenCalled_ReturnsOkResult()
        {
            // Act
            var okResult = _controller.Get();
            // Assert
            Assert.IsType<OkObjectResult>(okResult.Result);
        }
        [Fact]
        public void Get_WhenCalled_ReturnsAllItems()
        {
            // Act
            var okResult = _controller.Get().Result as OkObjectResult;
            // Assert
            var items = Assert.IsType<List<CompraItem>>(okResult.Value);
            Assert.Equal(3, items.Count);
        }
    }
}

Criamos uma instância do objeto ComprasController na classe de teste e essa é a classe que queremos testar. É importante observar aqui que esse construtor é chamado antes de cada método de teste, o que significa que estamos sempre redefinindo o estado do controlador e realizando o teste no novo objeto.

Isso é importante porque os métodos de teste não devem depender uns dos outros e devemos obter os mesmos resultados de teste, não importa quantas vezes executemos os testes e em que ordem os executamos.

Agora no menu Test clique em Windows->Test Explorer para abrir a janela do Test Explorer:

Nesta janela iremos executar e acompanhar a execução dos testes de unidade.

Conforme você executa, grava e executa novamente os testes, o Test Explorer exibe os resultados nos grupos padrão de Testes com Falha, Testes Aprovados, Testes Ignorados e Testes Não Executados. Você pode também alterar a forma como o Test Explorer agrupa seus testes.

O painel de detalhes de teste exibe as seguintes informações:

Se o teste falhar, o painel de detalhes também exibe:

Executando todos (Run All) os dois testes que criamos vamos obter o seguinte resultado:

Os dois métodos foram executandos sendo que 1 passou e 1 falhou.

O método Get_WhenCalled_ReturnOkResult passou e vemos um ícon verde indicando que tudo esta ok.

No método Get_WhenCalledResultAllItens falhou pois o resultado final esperado era de 3 itens mas como temos 5 itens o teste falhou.

Observe que a mensagem de erro indica que era esperado 3 mas retornou 5 e na janela à esquerda temos detalhes do teste indicando o arquivo e a linha onde ocorreu o erro.

Vamos corrigir o código do teste alterando o valor do esperado de 3 para 5 e executando novamente o teste.

Pronto ! agora temos os dois testes de unidades executados com sucesso.

É possível executar um teste específico apenas selecionando o teste desejado com o botão direito do mouse e clicando em “Run Selected Tests”.

Também é possível executar os testes via linha de comando, para isso é necessário instalar via NuGet o pacote xunit.runner.console.

Agora que já sabemos como testar na próxima parte do artigo vamos definir os demais testes unitários.

"Amo ao SENHOR, porque ele ouviu a minha voz e a minha súplica.
Porque inclinou a mim os seus ouvidos; portanto, o invocarei enquanto viver."
Salmos 116:1,2

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

Quer aprender a criar aplicações Web Dinâmicas usando a ASP .NET MVC 5 ?

Referências:


José Carlos Macoratti