Neste artigo veremos como criar casos de testes para uma API Asp.Net Core usando xUnit. |
O xUnit é uma ferramenta de teste de unidade gratuita, de código aberto e focada
na comunidade para aplicativos .NET. Por padrão, o .Net também fornece um modelo
de projeto xUnit para implementar casos de teste.
Os casos de teste de unidade baseiam-se na fórmula 'AAA' que significa 'Arrange', 'Act' e 'Assert' que numa tradução livre seria 'Organizar' , 'Agir' e 'Afirmar', onde :
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 será 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;
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].
O atributo [Theory] indica um teste parametrizado que é verdadeiro para um subconjunto de dados. Esses dados podem ser fornecidos de várias maneiras, mas o mais comum é com um atributo [InlineData]. Assim este atributo permite executar um método de teste várias vezes passando diferentes valores a cada vez como parâmetros.
Podemos ainda desativar um teste por qualquer motivo. Para isso basta definir a propriedade Skip no atributo Fact com o motivo que você desativou o teste (o motivo não é exibido).
[Fact(Skip = "Teste ainda não disponível")]
public void Teste()
{
}
|
Á medida que o número de seus testes aumenta, você pode organizá-los em grupos para que poder executar os testes juntos. O atributo [Trait] permite organizar os testes em grupos, criando nomes de categoria e atribuindo valores a eles.
[Fact(DisplayName = "Teste Numero 2")]
[Trait("Calculo", "Somar")]
public void Somar_DoisNumeros_RetornaNumero()
{ }
|
No Test Explorer, este teste aparecerá sob o título Calculo[Somar] (cada combinação Name/Value aparece como um cabeçalho separado no Test Explorer).
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 nome nos informa mais sobre o cenário de teste;(opcional)
3- A última parte do nome é o nome do teste ou o resultado esperado;
Exemplo : Soma_DoisNumerosInteiros_RetornaNumeroInteiro
Temos assim uma apresentação dos principais recursos do XUnit para realização de testes de unidade.
Criando o projeto Web API
Vamos iniciar criando um solução em branco usando o template Blank Solution com o nome MinhaApi.
A seguir vamos incluir na solução um projeto pronto e criado com o template ASP.NET Core Web App com o nome MinhaApi.WebApi. Este projeto contem a API que desejamos testar.
Depois disso vamos criar e incluir um novo projeto usando o template
xUnit Test Project com o nome
MinhaApi.Tests
Ao final teremos uma solução com dois projetos:
Vamos criar as seguintes pastas no projeto Tests:
Controllers
Helpers
MockData
Services
- A pasta
Controllers contém os testes relacionados com os
Controllers;
- A pasta Helpers contém o código comum usado;
- A pasta MockData contém arquivos relacionados com
os dados fake que podem ser usados para o response e request no testes;
- A pasta Services contém os arquivos de testes
para o serviço relacionados com a aplicação;
Agora precisamos instalar em nosso projeto MinhaApi.Tests os seguintes pacotes nuget :
Para instalar podemos usar os seguintes comandos:
Ao final teremos os seguintes pacotes instalados em nosso projeto :
Agora inclua uma referência ao projeto de Web API no projeto de testes:
Criando os casos de testes
A nossa API representa pelo controlador TarefasController possui o seguinte código :
[Route("api/[controller]")] [ApiController] public class TarefasController : ControllerBase { private readonly ITarefaService _tarefaService; public TarefasController(ITarefaService tarefaService) { _tarefaService = tarefaService; } [Route("tarefas")] [HttpPost] |
Aqui o método GetTodasTarefasAsync() retorna uma coleção de objetos Tarefa.
Vamos iniciar criando dados fictícios para tarefas na pasta MockData do projeto de testes.
Crie a classe TarefasMockData na pasta MockData com o código abaixo:
using MinhaApi.WebApi.Data.Entities; namespace MinhaApi.Tests.MockData; public class TarefasMockData |
Na pasta Controllers do projeto de testes vamos criar a classe TestTarefaController e a seguir vamos definir casos de testes para o método GetTodasTarefasAsync da API.
1- GetTodasTarefasAsync_ShouldReturn200Status()
public class TestTarefaController { [Fact] public async Task GetTodasTarefasAsync_ShouldReturn200Status() { /// Arrange var tarefaService = new Mock<ITarefaService>(); tarefaService.Setup(_ => _.GetTarefasAsync()).ReturnsAsync(TarefasMockData.GetTarefas()); var sut = new TarefasController(tarefaService.Object);
/// Act // /// Assert |
Esse é nosso primeiro caso de teste para o método GetTodasTarefasAsync(). Vamos entender o código :
O método é
decorado com um atributo como 'Fact', isso
determina que o método deve ser executado pelo executor de teste.
A convenção de nomenclatura recomendada do método de teste é
'Nome do método a ser testado' + 'Nome
do teste ou o resultado esperado;'.
Aqui podemos
observar o código separado com base na fórmula 'AAA'.
- Queremos testar o método 'GetTodasTarefasAsync()' do controlador e
sabemos que este método depende do resultado do método
'TarefaService.GetTarefasAsync()', o que significa que o método Action do
nosso controlador não se importa com a implementação deste método;
Assim, criamos uma instância simulada de 'ITarefaService' e, em seguida, simulamos o resultado do método 'ITarefaService.GetTarefasAsync()'.
var
tarefaService = new Mock<ITarefaService>();
tarefaService.Setup(_ =>
_.GetTarefasAsync()).ReturnsAsync(TarefasMockData.GetTarefas());
Finalmente cria a instância de 'TarefasController'. Aqui a variável 'sut' significa 'System Under Test' apenas uma convenção de nomenclatura recomendada.
var
sut = new TarefasController(tarefaService.Object);
No código invocamos o método Action do controlador 'GetTodasTarefasAsync()',
e, como nosso método retorna 'OkObjectResult' para o status 200, aqui
fazemos a conversão explicita do resultado :
var result = (OkObjectResult)await
sut.GetTodasTarefasAsync();
A seguir verificamos o resultado esperado que é 200 como código de status.
result.StatusCode.Should().Be(200);
Acionando o Test Explorer no Visual Studio 2022 podemos verificar o teste criado:
Executando teremos o seguinte resultado:
Vamos escrever mais um caso teste para 'TarefaController.GetTodasTarefasAsync()' considerando o retorno do status 204 que ocorre quando não há dados para retornar como resposta.
Para isso vamos incluir na classe TarefasMockData um método que retorna uma lista vazia de objetos Tarefa:
public static List<Tarefa> GetTarefasVazia() { return new List<Tarefa>(); } |
A seguir vamos criar o caso de teste para o retorno 204 definindo na classe TestTarefaController o método GetTodasTarefasAsync_ShouldReturn204NoContentStatus :
[Fact] public async Task GetTodasTarefasAsync_ShouldReturn204NoContentStatus() { /// Arrange var tarefaService = new Mock<ITarefaService>(); tarefaService.Setup(_ => _.GetTarefasAsync()).ReturnsAsync(TarefasMockData.GetTarefasVazia()); var sut = new TarefasController(tarefaService.Object);
/// Act |
Neste código :
- Estamos mocando
o método 'ITarefaSerivice.GetTarefasAsync()' para retornar a coleção de dados
vazia.
- Invocamos o método 'TarefaController.GetTodasTarefasAsync()'.
- Esperando que nosso código de status seja '204'
- Na verificação de 'ITarefaService.GetTarefasAsync()' é chamada exatamente uma
vez dentro do nosso método 'TarefaConroller.GetTarefasAsync()'.
Executando no Test Explorer este caso teste obtemos o resultado abaixo:
Na continuação do artigo iremos criar casos de teste para testar o serviço.
"Nada façais por contenda ou por vanglória, mas por
humildade; cada um considere os outros superiores a si mesmo."
Filipenses 2:3
Referências: