C# - Realizando Testes Unitários com XUnit
Neste artigo veremos como realizar testes de unidade usando o XUnit. |
Eu apresentei os conceitos básicos sobre XUnit neste artigo: C# - XUnit Básico e hoje vamos continuar mostrando como realizar testes unitários com essa ferramenta.
Assim, veremos uma aplicação prática usando um projeto Console na linguagem C# e o framework XUnit.
Criando o projeto C#
Vamos criar um projeto do tipo Console para .NET Core no VS 2019 Community chamado CShp_Calculos;
Neste projeto vamos criar uma classe chamada Calculo com o seguinte código:
namespace CShp_Calculos
{
public static class Calculo
{
public static double Somar(double numero1, double numero2)
{
return (numero1 + numero2);
}
public static double Subtrair(double numero1, double numero2)
{
return (numero1 - numero2);
}
public static double Multiplicar(double numero1, double numero2)
{
return (numero1 * numero2);
}
public static double Dividir(double numero1, double numero2)
{
return (numero1 / numero2);
}
public static bool IsNumeroPar(int numero)
{
return numero % 2 == 0;
}
}
}
|
Temos aqui uma classe bem simples com algumas operações matemáticas que iremos usar apenas para ilustrar a utilização do XUnit.
Criando o projeto de Teste
Vamos agora criar o projeto de teste na mesma solução.
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 CShp_CalculosTest 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 Cshp_Calculos.
Clique com o botão direito do mouse sobre o projeto CShp_CalculosTest e a seguir clique em Add Reference;
Na janela Reference Manager marque o projeto Cshp_Calculos e clique em OK:
Ao final teremos a nossa solução contendo os dois projetos.
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).
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 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;
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;
3- A última parte do nome é o resultado esperado;
Exemplo : Soma_DoisNumerosInteiros_RetornaNumeroInteiro
Criando os testes de unidade
Vamos criar no projeto CShp_CalculosTest a classe CalculoUnitTest.
Agora vamos criar os casos de testes iniciando com o teste de unidade para o método Somar.
Crie na classe CalculoUnitTest o código a seguir que representa nosso primeiro caso de teste de unidade:
Neste caso de teste de unidade estamos somando os números 2.9 e 3.1 e esperamos o resultado igual a 6.
Estamos usando
o objeto Calculo e o método estático
Somar na classe de
teste e essa é a classe que queremos testar. É importante observar aqui que
fazemo isso antes de cada método de teste, o que significa que estamos
sempre redefinindo o estado da classe Calculo.
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:
Temos a janela exibindo no lado esquerdo o nosso teste de unidade e acima um menu com opções que permite controlar a execução dos testes. No lado direito temos os detalhes do teste selecionado.
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:
Podemos executar todos os testes ou apenas um teste selecioando. Executando o teste que criamos vamos obter o seguinte resultado:
Temos que o teste passou e a sua duração.
Vamos agora definir os casos de testes para os demais métodos da classe Calculo.
using CShp_Calculos;
using Xunit;
namespace CShp_CalculosTest
{
public class CalculoUnitTest
{
[Fact]
public void Somar_DoisDouble_RetornaDouble()
{
// Arrange
var num1 = 2.9;
var num2 = 3.1;
var valorEsperado = 6;
// Act
var soma = Calculo.Somar(num1, num2);
//Assert
Assert.Equal(valorEsperado, soma);
}
[Fact]
public void Subtrair_DoisDouble_RetornaDouble()
{
// Arrange
var num1 = 2.9;
var num2 = 3.1;
var valorEsperado = -0.2;
// Act
var subtracao = Calculo.Subtrair(num1, num2, 1);
//Assert
Assert.Equal(valorEsperado, subtracao);
}
[Fact]
public void Multiplicar_DoisDouble_RetornaDouble()
{
// Arrange
var num1 = 2.9;
var num2 = 3.1;
var valorEsperado = 8.99;
// Act
var mult = Calculo.Multiplicar(num1, num2);
//Assert
Assert.Equal(valorEsperado, mult, 2);
}
[Fact]
public void Dividir_DoisDouble_RetornaDouble()
{
// Arrange
var num1 = 2.9;
var num2 = 3.1;
var valorEsperado = 0.94; //Rounded value
// Act
var div = Calculo.Dividir(num1, num2);
//Assert
Assert.Equal(valorEsperado, div, 2);
}
}
}
|
Definimos 4 casos de testes sendo que um já foi executado. Abrindo o Teste Explorer veremos:
Executando todos os testes iremos obter o seguinte resultado:
Observe que o teste para o método Subtrair falhou, e, analisando os detalhes vemos que era esperado o valor -0,2 e foi obtido o valor 0,2000000000000018. Temos um problema de precisão.
Para resolver isso basta ajustar o código incluindo o valor da precisão igual a no Assert:
[Fact]
public void Subtrair_DoisDouble_RetornaDouble()
{
// Arrange
var num1 = 2.9;
var num2 = 3.1;
var valorEsperado = -0.2;
// Act
var subtracao = Calculo.Subtrair(num1, num2);
//Assert
Assert.Equal(valorEsperado, subtracao, 1);
}
|
Executando novamente veremos que agora todos os testes vão passar:
que todos os testes passaram e temos também o tempo de execução de cada teste.
Vamos agora definir um caso de teste usando o atributo [Theory] para o método Somar.
[Theory] [InlineData(1, 2, 3)] [InlineData(-4, -6, -10)] [InlineData(-2, 2, 0)] [InlineData(int.MinValue, -1, int.MaxValue)] public void Somar_DoisNumerosRelativos_RetornaNumeroRelativo(int num1, int num2, int valorEsperado) { //Act var resultado = Calculo.Somar(num1, num2); //Assert Assert.Equal(valorEsperado, resultado); } |
Agora ao invés
de especificar os valores para somar (num1 e num2)
no corpo do teste, passamos os valores como parâmetros para o teste.
Passamos também o resultado esperado do cálculo para usar na chamda do
Assert.Equal().
Aqui os dados dados são fornecidos pelo atributo [InlineData]
onde cada instância de [InlineData] criará uma
execução separada do método
Somar_DoisNumerosRelativos_RetornaNumeroRelativo.
Os valores passados no construtor de [InlineData]
são usados como parâmetros para o método, e, a ordem dos parâmetros no
atributo corresponde à ordem em que eles são fornecidos ao método.
Vejamos como a janela Test Explorer exibe esse teste de unidade:
Executando o teste criado iremos obter:
Note que o teste de unidade falhou para os valores [InlineData(int.MinValue, -1, int.MaxValue)] pois era esperado o valor 2147483647 e foi obtido o valor -2147483649.
Vamos corrigir definindo alterando os parâmetros para [InlineData(int.MinValue, +1, int.MinValue +1)].
Executando novamente agora teremos o resultado a seguir:
Temos assim uma apresentação dos principais recursos do XUnit para realização de testes de unidade.
Pegue o código do projeto aqui: CShp_Calculos.zip
"Qual é
mais fácil? Dizer ao paralítico: Estão perdoados os teus pecados, ou dizer:
Levanta-te, toma o teu leito e anda?
Ora, para que saibais que o Filho do Homem tem sobre a terra autoridade para
perdoar pecados — disse ao paralítico: Eu te mando: Levanta-te, toma o teu
leito e vai para tua casa."
Marcos 2:9-11
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:
Super DVD Vídeo Aulas - Vídeo Aula sobre VB .NET, ASP .NET e C#
Super DVD C# - Recursos de aprendizagens e vídeo aulas para C#
Testes de Software - Criando e Executando testes unitários ... - Macoratti
Visual Studio - Criando testes unitários - Macoratti
VB .NET - TDD - Test Drive Development - Macoratti
.NET - Usando MOQ em testes unitários - I - Macoratti
Testes de Software - Macoratti
Testes em desenvolvimento de Software - Macoratti
Eng, de Software - Macoratti