Um hábito que estou procurando cultivar nos últimos tempos é sempre adicionar testes unitários nas minhas aplicações. Qualquer desenvolvedor sabe (ou deveria saber) os benefícios de se adicionar testes unitários em uma aplicação, mas infelizmente não são todos que seguem esta filosofia. Para ajudar nisso, criarei uma série de artigos falando sobre as bibliotecas de testes unitários do C#, começando pela NUnit.
Conhecendo a biblioteca NUnit
O NUnit é uma biblioteca open source de teste unitários, que nasceu como um porte para o .NET da biblioteca de teste unitários do Java, a JUnit. Com o tempo ela foi sendo reescrita em C#. Agora, na versão atual, a 3, a biblioteca foi totalmente reescrita para que fosse adicionado novos recursos e fizesse uso de recursos fornecidos pela plataforma .NET.
Devido a sua gama de recursos e facilidade de uso, no momento, é a biblioteca de testes unitários mais popular do .NET.
Para conhecê-la, vamos colocar a mão na massa.
Criando o projeto que será testado
Diferente de outras bibliotecas, os testes unitários devem ser adicionados em um projeto a parte. O .NET fornece templates de projetos para algumas bibliotecas de testes, incluindo o NUnit, mas caso seja necessário definir isso manualmente, basta criar um projeto de biblioteca de classes.
Por necessitar de dois projetos, vamos iniciar a criação da aplicação utilizada neste artigo com a criação da solução:
dotnet new sln -n teste-unitario-com-nunit
Dentro da pasta da solução, crie uma biblioteca de classes:
dotnet new classlib -n calculos
No projeto criado, renomeie o arquivo Class1
para Calculadora
e nela adicione os métodos abaixo:
public class Calculadora
{
public int Soma(int operador1, int operador2) => operador1 + operador2;
public int Subtracao(int operador1, int operador2) => operador1 - operador2;
public int Multiplicacao(int operador1, int operador2) => operador1 * operador2;
public int Divisao(int dividendo, int divisor) => dividendo / divisor;
public (int quociente, int resto) RestoDivisao(int dividendo, int divisor) => (dividendo / divisor, dividendo % divisor);
}
Curso C# (C Sharp) - TDD
Conhecer o cursoPor fim, adicione o projeto na solução:
dotnet sln teste-unitario-com-nunit.sln add calculos/calculos.csproj
Criando o projeto de teste
Como disse, o .NET fornece alguns templates de projetos de teste para algumas bibliotecas. Para a NUnit, o projeto pode ser criado com o comando abaixo:
dotnet new nunit -n calculos.tests
Adicione este projeto na solução:
dotnet sln teste-unitario-com-nunit.sln add calculos.tests/calculos.tests.csproj
E adicione ao projeto a referência da nossa biblioteca de classes, o projeto calculos:
dotnet add calculos.tests/calculos.tests.csproj reference calculos/calculos.csproj
Agora renomeie o arquivo UnitTest1.cs para CalculadoraTest.cs e altere o código do arquivo para:
using NUnit.Framework;
using calculos;
namespace calculos.tests
{
[TestFixture]
public class CalculadoraTest
{
[Test]
public void Soma_DeveRetornarOValorCorreto()
{
Calculadora c = new Calculadora();
var resultado = c.Soma(10, 20);
//Verifica se o resultado é igual a 30
Assert.AreEqual(30, resultado);
}
}
}
No código acima, o atributo [TestFixture]
indica que a classe contém testes unitários. Já o atributo [Test]
indica que o método é um método de teste. É através desses atributos que o comando dotnet test
e o Test Explorer do Visual Studio conseguem localizar os testes unitários do projeto.
Dentro do nosso método de teste, é utilizado a classe estática Assert
para ter acesso ao método AreEqual
, que verifica se o primeiro parâmetro é igual ao segundo.
Na versão 3 da biblioteca foi introduzido a sintaxe “constraint”, onde o teste acima pode ser escrito da seguinte forma:
[Test]
public void Soma_DeveRetornarOValorCorreto()
{
Calculadora c = new Calculadora();
var resultado = c.Soma(10, 20);
//Verifica se o resultado é igual a 30
Assert.That(30, Is.EqualTo(resultado));
}
Como a biblioteca recomenda o uso desta sintaxe, todos os testes mostrados neste artigo implementarão ela.
Com o teste definido, podemos executá-lo com o comando abaixo:
dotnet test
O resultado será:
O nosso único teste passou.
Um outro recurso adicionado na última versão da biblioteca é a possibilidade de agrupar os comparadores:
[Test]
public void Divisao_DeveRetornarOValorCorreto()
{
Calculadora c = new Calculadora();
var resultado = c.RestoDivisao(10, 3);
//Verifica se o quociente da divisão é 3 e o resto 1
Assert.Multiple(() =>
{
Assert.That(3, Is.EqualTo(resultado.quociente));
Assert.That(1, Is.EqualTo(resultado.resto));
});
}
A vantagem de se agrupar comparadores é que caso qualquer um falhe, o teste não é finalizado. A falha é salva e os demais testes são executados. Ao final, o resultado de todos é agrupado. O teste só será finalizado caso seja gerada alguma exceção não tratada. Além disso, o bloco aceita outros códigos além de asserts, mas não é permitido o uso os asserts abaixo:
-
Assert.Pass
; -
Assert.Ignore
; -
Assert.Inconclusive
; -
Assume.That
.
Definindo vários testes unitários de uma só vez
No momento definimos apenas dois testes. Seguindo a lógica mostrada, para cada teste unitário é necessário definir um método de teste. Felizmente, o NUnit possui o atributo TestCase
, que nos permite definir mais de um teste, variando apenas os argumentos:
[TestCase(1)]
[TestCase(2)]
[TestCase(3)]
public void RestoDivisao_DeveRetornarZero(int value)
{
Calculadora c = new Calculadora();
var resultado = c.RestoDivisao(12, value);
//Verifica se o resto da divisão é 0
Assert.That(0, Is.EqualTo(resultado.resto));
}
Desta forma, para o NUnit estão sendo definidos três métodos de teste, por isso que ao executá-los é mostrado cinco testes:
Por fim, para que a cobertura dos testes seja 100%, vamos definir método de testes para os outros métodos da nossa classe de Calculadora
:
using NUnit.Framework;
using calculos;
namespace calculos.tests
{
[TestFixture]
public class CalculadoraTest
{
[Test]
public void Soma_DeveRetornarOValorCorreto()
{
Calculadora c = new Calculadora();
var resultado = c.Soma(10, 20);
//Verifica se o resultado é igual a 30
Assert.That(30, Is.EqualTo(resultado));
}
[Test]
public void RestoDivisao_DeveRetornarOValorCorreto()
{
Calculadora c = new Calculadora();
var resultado = c.RestoDivisao(10, 3);
//Verifica se o quociente da divisão é 3 e o resto 1
Assert.Multiple(() =>
{
Assert.That(3, Is.EqualTo(resultado.quociente));
Assert.That(1, Is.EqualTo(resultado.resto));
});
}
[TestCase(1)]
[TestCase(2)]
[TestCase(3)]
public void RestoDivisao_DeveRetornarZero(int value)
{
Calculadora c = new Calculadora();
var resultado = c.RestoDivisao(12, value);
//Verifica se o resto da divisão é 0
Assert.That(0, Is.EqualTo(resultado.resto));
}
[Test]
public void Subtracao_DeveRetornarOValorCorreto()
{
Calculadora c = new Calculadora();
var resultado = c.Subtracao(20, 10);
//Verifica se o resultado é igual a 10
Assert.That(10, Is.EqualTo(resultado));
}
[Test]
public void Divisao_DeveRetornarOValorCorreto()
{
Calculadora c = new Calculadora();
var resultado = c.Divisao(100, 10);
//Verifica se o resultado é igual a 10
Assert.That(10, Is.EqualTo(resultado));
}
[Test]
public void Multiplicacao_DeveRetornarOValorCorreto()
{
Calculadora c = new Calculadora();
var resultado = c.Multiplicacao(5, 2);
//Verifica se o resultado é igual a 10
Assert.That(10, Is.EqualTo(resultado));
}
}
}
Curso Teste de Software Básico
Conhecer o cursoDemais comparadores do NUnit
O exemplo mostrado aqui é simples, mas ele demonstra a importância da definição de testes em uma aplicação. Agora sempre que os métodos da classe Calculadora
forem alterados, os testes podem ser executados e verificados se algum dos comportamentos existentes foi impactado com a alteração.
Neste exemplo é utilizado apenas o comparador de igualdade, que é um dos mais comuns. Você pode ver os demais comparadores disponíveis na documentação do NUnit.
No meu próximo artigo abordarei o XUnit, até lá :)