Teste de Unidade
Tags: testing, csharp, xunit, api
Published: 2021-04-25 19:30:32

Teste de Unidade: É o teste da menor unidade testável de um programa.

Em português é comum falar teste unitário, porém o mais correto é o termo teste de unidade. Teste unitário da a sensação que é apenas um teste, no entanto podemos criar vários cenários para testar o seu método.

Um teste de unidade precisa ser:

* Rápido: Não é incomum para projetos maduros ter milhares de testes de unidade. Os testes de unidade devem levar muito pouco tempo para serem executados. Milissegundos.

* Isolado: Testes de unidade são autônomos e não têm dependências de nenhum fator externo, como um sistema de arquivos ou o banco de dados. Mesmo um teste de integração, as dependências externas são resolvidas com o uso do InMemoryDataBase ou com mocks. 

* Repetível: A execução de um teste de unidade deve ser consistente com seus resultados, ou seja, ele sempre retornará o mesmo resultado se você não alterar nada entre execuções. Se o teste hora passa, e hora não, talvez ele esteja compartilhando estado de algum objeto.
 
* Verificação automática: O teste deve ser capaz de detectar automaticamente se ele foi aprovado ou reprovado sem nenhuma interação humana.

* Em tempo hábil: Um teste de unidade não deve levar muito tempo para ser escrito comparado com o código que está sendo testado.  Se você demorar muito tempo para escrever o teste, considere um design mais estável ou um refactoring.

## Como nomear os Testes?

Modelo 01:
ObjetoEmTeste_MetodoComportamentoEmTeste_ComportamentoEsperado 
Ex: Pedido_AdicionarItem_DeveExibirErroSeItemJaAdicionado

Modelo 02:
MetodoEmTeste_EstadoEmTeste_ComportamentoEsperado
Ex: AdicionarItem_ItemJaExistente_DeveExibirErroSeItemJaAdicionado

## Padrão AAA

O padrão AAA auxilia na organização do teste e deixa ele mais legível.

Arrange: onde é preparado os objetos para serem testados.
Act: onde fica a ação a ser executada.
Assert: onde ficam as comparações.

Evite usar os métodos de Setup e Teardown para criar objetos utilizados no teste, assim você terá:
* Menos confusão ao ler os testes, uma vez que todo o código está visível dentro de cada teste;
* Menos chance de configurar demais ou de menos para o teste específico;
* Menos chance de compartilhar o estado entre os testes, que cria dependências indesejadas entre eles.

Evite o uso de mais de um assert no teste, pois assim você garante que não está declarando vários cenários no mesmo teste.

>  Uma exceção a regra é um assert sobre propriedades de um objeto.

## Frameworks para teste

* MsTest: Desenvolvido pela Microsoft.
* nUnit: Desenvolvido e mantido pelo .Net Foundations. 
* xUnit: Desenvolvido pelos mesmos criadores do nUnit e é o mais recente framework para testes.

Segue comparação entre os frameworks no [link](https://xunit.net/docs/comparisons).

Apesar da Microsoft ter desenvolvido o MsTest, atualmente ela recomenda o uso do xUnit.

## Exemplos com xUnit
Vamos ver agora alguns exemplos de teste de unidade, usando o framework xUnit e os padrões de nomenclatura e organização dos testes.

O teste abaixo verifica se um cpf é válido.
```
[Fact]
public void SocialNumber_IsValid_ReturnTrueForValidValue()
{
	//Arrange          
	//Act
	var result = SocialNumber.IsValid("52812349000");

	//Assert
	Assert.True(result);
}
```

No xUnit para testar mais de um valor pode-se utilizar o Theory, assim seu teste fica mais limpo.

O teste a seguir verifica se cada cpf informado no InlineData é válido.
```
[Theory]
[InlineData("557.287.970-32")]
[InlineData("016.118.090-61")]
[InlineData("143.504.220-48")]
[InlineData("660.352.030-35")]
[InlineData("93355758019")]
[InlineData("77228802071")]
[InlineData("24780314003")]    
public void SocialNumber_IsValid_ReturnTrueForValidValues(string value)
{
	//Arrange          
	//Act
	var result = SocialNumber.IsValid(value);

	//Assert
	Assert.True(result);
}
```

Para finalizar, um exemplo testando exceções.

O teste a seguir, verifica se uma determinada exceção é lançada ao informar um valor nulo.
```
[Fact]
public void SocialNumber_Constructor_ThrowsExceptionForNullValues()
{
	//Arrange          
	//Act   
	//Assert
	var ex = Assert.Throws<ArgumentNullException>(() => SocialNumber.FromString(null));
	Assert.Equal("Value cannot be null. (Parameter 'value')", ex.Message);
}
```
Caso queira ver o código completo, segue o [link] (https://github.com/alsouza93/LoanBookApi/blob/main/LoanBook.Api.UnitTests/SocialNumberTest.cs) do GitHub.


## Referências

Recomendo a leitura deste artigo da Microsoft. Nada melhor do que beber a água direto da fonte.

https://docs.microsoft.com/en-us/dotnet/core/testing/unit-testing-best-practices

Se quiser se aprofundar mais no assunto, também recomendo o curso "Dominando os Testes de Software" do Eduardo Pires na plataforma https://desenvolvedor.io.