Mocks com Moq
ResumoUm mock é uma implementação falsa de uma dependência (interface), criada em tempo de teste. Com Moq você configura o comportamento (
Setup) e verifica interações (Verify), isolando a unidade sob teste de banco, rede e outros serviços.
1. Objetivos de Aprendizagem#
- Criar mocks de interfaces com Moq.
- Configurar retornos com
Setup. - Verificar chamadas com
Verify.
2. Pré-requisitos#
- Interfaces (Módulo 05), DI (Módulo 12) e xUnit (tópico 15.1).
3. Conceito#
Ao testar um serviço que depende de IRepositorio, você não quer tocar o banco real. Um mock implementa a interface em memória, com comportamento que você define:
Setup: "quando chamarem este método com estes argumentos, retorne isto".Verify: "confirme que este método foi chamado (quantas vezes)".
O porquê: testes rápidos, determinísticos e focados na lógica da unidade — é aqui que a injeção de dependência (depender de interfaces) paga dividendos.
4. Mão na Massa#
4.1. Setup#
dotnet new xunit -n Catalog.Tests3
dotnet add Catalog.Tests3 package Moq
4.2. Implementação Passo a Passo#
- Dependência e serviço:
public interface IRepositorioProduto
{
Task<decimal?> ObterPrecoAsync(Guid id);
}
public class ServicoPreco
{
private readonly IRepositorioProduto _repo;
public ServicoPreco(IRepositorioProduto repo) => _repo = repo;
public async Task<decimal> ComImpostoAsync(Guid id)
{
var preco = await _repo.ObterPrecoAsync(id)
?? throw new InvalidOperationException("Produto sem preço.");
return preco * 1.1m;
}
}
- Teste com mock (
Setup+Verify):
using Moq;
using Xunit;
public class ServicoPrecoTests
{
[Fact]
public async Task ComImposto_AplicaSobrePrecoDoRepositorio()
{
// Arrange
var id = Guid.NewGuid();
var repo = new Mock<IRepositorioProduto>();
repo.Setup(r => r.ObterPrecoAsync(id)).ReturnsAsync(100m);
var servico = new ServicoPreco(repo.Object);
// Act
var resultado = await servico.ComImpostoAsync(id);
// Assert
Assert.Equal(110m, resultado);
repo.Verify(r => r.ObterPrecoAsync(id), Times.Once);
}
}
4.3. Executando#
dotnet test
5. Exemplo Completo#
O mock de IRepositorioProduto retorna 100m sem tocar o banco; o teste valida o cálculo (110m) e confirma que o repositório foi consultado uma vez. Rápido, isolado e determinístico.
6. Boas Práticas e Armadilhas#
| Faça | Evite |
|---|---|
| Mockar interfaces (fronteiras: repositórios, serviços externos) | Mockar tipos concretos ou tudo indistintamente |
Verificar interações relevantes com Verify | Sobre-especificar (testes frágeis que quebram a cada refatoração) |
| Usar mocks para I/O (banco, rede) | Mockar lógica de negócio que deveria ser testada de verdade |
Atençãoteste comportamento, não implementação. Verificar chamadas demais acopla o teste aos detalhes internos e o torna frágil. Prefira asserir o resultado observável sempre que possível.
7. Segurança e Produção#
- Isolar dependências externas mantém a suíte rápida e confiável no CI. Testes que dependem de rede/banco real são lentos e instáveis — reserve-os para testes de integração dedicados.
8. Exercícios#
- Fácil: crie um mock que retorna um valor fixo com
Setup. - Médio: verifique que um método do mock foi chamado exatamente uma vez.
- Desafio: teste o caminho de erro (repositório retorna
null) e confirme a exceção comAssert.Throws/ThrowsAsync.
9. Resumo#
Moq cria implementações falsas de interfaces: Setup define retornos, Verify confere interações. Isso isola a unidade de I/O, tornando os testes rápidos e determinísticos. Teste comportamento, não implementação.
10. Próximos Passos#
Você concluiu os fundamentos de .NET. Está pronto para o curso de Web API com ASP.NET Core (livro.zip), que aplica tudo isso na prática.
11. Referências#
- Moq — Guia rápido
- Microsoft Learn — Testes unitários com Moq e xUnit
- Microsoft Learn — Testes de integração no ASP.NET Core