Unit testing
Testes unitários são pequenos testes que verificam o comportamento de um método ou classe. Quando o código que você está testando depende de outros métodos ou classes, os testes unitários dependem do mocking dessas outras classes, de modo que o teste se concentre apenas em uma coisa por vez.
Por exemplo, a classe TodoController
tem duas dependências: uma ITodoItemService
e o UserManager
. O TodoItemService
, por sua vez, depende do ApplicationDbContext
. (A idéia de que você pode desenhar uma linha de TodoController
> TodoItemService
> ApplicationDbContext
é chamada de dependency graph [gráfico de dependência]).
Quando a aplicação é executada normalmente, o contêiner de serviço do ASP.NET Core e o sistema de injeção de dependência injetam cada um desses objetos no dependency graph quando o TodoController
ou oTodoItemService
é criado.
Quando você escreve um teste unitário, você precisa manipular o dependency graph sozinho. É típico fornecer versões somente de teste ou "mockadas" dessas dependências. Isso significa que você pode isolar apenas a lógica da classe ou método que está testando. (Isto é importante! Se você está testando um serviço, você não quer também acidentalmente gravar dados no seu banco de dados.)
Criando um projeto de testes
É uma boa prática criar um projeto separado para seus testes, para que eles sejam mantidos separados do código da aplicação. O novo projeto de teste deve estar em um diretório próximo (não dentro) do diretório do seu projeto principal.
Se você está atualmente no diretório do seu projeto, o comando cd
sobe um nível. (Este diretório raiz também será chamado AspNetCoreTodo
). Em seguida, use este comando para criar um novo projeto de teste:
O xUnit.NET é um framework de teste popular para código .NET que pode ser usado para escrever testes unitários e de integração. Como todos os outros, é um conjunto de pacotes NuGet que podem ser instalados em qualquer projeto. O modelo dotnet new xunit
já inclui tudo o que você precisa.
Sua estrutura de diretórios deve estar assim:
Como o projeto de teste usará as classes definidas em seu projeto principal, você precisará adicionar uma referência ao projeto AspNetCoreTodo
:
Exclua o arquivo UnitTest1.cs
criado automaticamente. Você está pronto para escrever seu primeiro teste.
Se você estiver usando o Visual Studio Code, talvez seja necessário fechar e reabrir a janela do Visual Studio Code para que o autocomplete volte a funcionar no novo projeto.
Escrevendo um teste de serviço
Dê uma olhada na lógica do método AddItemAsync()
do TodoItemService
:
Esse método faz uma série de decisões ou suposições sobre o novo item (em outras palavras, executa a lógica de negócio no novo item) antes de realmente salvá-lo no banco de dados:
A propriedade
UserId
deve ser configurada para o ID do usuárioNovos itens devem estar sempre incompletos (
IsDone = false
)O título do novo item deve ser copiado de
newItem.Title
Novos itens devem sempre ser finalizados daqui a 3 dias
Imagine se você ou alguém refatorasse o método AddItemAsync()
e se esquecesse dessa lógica de negócios. O comportamento do seu aplicativo pode mudar sem você perceber! Você pode evitar isso escrevendo um teste que verifique duas vezes se essa lógica de negócio não foi alterada (mesmo que a implementação interna do método seja alterada).
Pode parecer improvável agora que você possa introduzir uma mudança na lógica de negócio sem perceber, mas fica muito mais difícil acompanhar as decisões e suposições em um projeto grande e complexo. Quanto maior o seu projeto, mais importante é ter verificações automatizadas que garantem que nada mudou!
Para escrever um teste unitário que irá verificar a lógica no TodoItemService
, crie uma nova classe no seu projeto de teste:
AspNetCoreTodo.UnitTests/TodoItemServiceShould.cs
Existem muitas maneiras diferentes de nomear e organizar testes, todos com diferentes prós e contras. Eu gosto de usar postfixing em minhas classes de teste com
Should
para criar uma frase legível com o nome do método de teste, mas sinta-se livre para usar seu próprio estilo!
O atributo [Fact]
vem do pacote Nuget xUnit.NET e marca este método como um método de teste.
O TodoItemService
requer um ApplicationDbContext
, que normalmente está conectado ao seu banco de dados. Você não vai querer usar isso para testes. Em vez disso, você pode usar o provider do Entity Framework Core's in-memory database em seu código de teste. Como todo o banco de dados existe na memória, ele é apagado toda vez que o teste é reiniciado. E, como é um provider do Entity Framework Core, o TodoItemService
não saberá a diferença!
Use um DbContextOptionsBuilder
para configurar o in-memory database provider e, em seguida faça uma chamada para AddItemAsync()
:
A última linha cria um novo to-do item chamado Testing?
, E diz ao serviço para salvá-lo no banco de dados (in-memory).
Para verificar se a lógica de negócios foi executada corretamente, escreva mais alguns códigos abaixo do bloco using
existente:
A primeira assertion é uma verificação de integridade: nunca deve haver mais de um item salvo no in-memory database. Supondo que isso seja verdade, o teste recupera o item salvo com "FirstAsync" e, em seguida, afirma que as propriedades estão definidas para os valores esperados.
Tanto os testtes unitários quanto os de integração geralmente seguem o padrão AAA (Arrange-Act-Assert): objetos e dados são configurados primeiro, depois alguma ação é executada e, finalmente, o teste verifica (confirma) que o comportamento esperado ocorreu.
Fazer Asserting em valores de data e hora é um pouco complicado, uma vez que comparar duas datas para igualdade falhará se até mesmo os milissegundos forem diferentes. Em vez disso, o teste verifica se o valor de DueAt
está a menos de um segundo do valor esperado.
Executando o teste
No terminal, execute este comando (verifique se você ainda está no diretório AspNetCoreTodo.UnitTests
):
O comando test
examina o projeto atual para testes (marcado com atributos [Fact]
neste caso), e executa todos os testes que encontrar. Você verá uma saída semelhante a:
Agora você tem um teste fornecendo cobertura de teste do TodoItemService
. Como um desafio extra, tente escrever testes de unidade que garantam:
O método
MarkDoneAsync()
retorna false se for passado um ID que não existeO método
MarkDoneAsync()
retorna true quando um item válido é marcado como completoO método
GetIncompleteItemsAsync()
retorna apenas os itens pertencentes a um usuário em particular
Last updated
Was this helpful?