Neste artigo vou explicar como fazer teste unitário para Entity Framework utilizando o pacote Moq do C#. Com ele podemos simular comportamentos específicos em nossas classes e configurar cenários de testes.
Antes de começar, vamos criar uma estrutura simples de objeto, entidade e respositório com Entity Framework.
namespace LuizPauloPradoBlog.Repository.Model
{
public class Car
{
public int Id { get; set; }
public string Model { get; set; }
public string Name { get; set; }
public int YearOfManufacture { get; set; }
}
}
namespace LuizPauloPradoBlog.Repository.Context
{
public class CarShoppingContext : DbContext
{
public CarShoppingContext() { }
public virtual DbSet<Car> Cars { get; set; }
}
}
namespace LuizPauloPradoBlog.Repository
{
public class CarRepository : ICarRepository
{
private readonly CarShoppingContext _context;
public CarRepository(CarShoppingContext context)
{
_context = context;
}
public IList<Car> GetAll()
{
return _context.Cars.ToList();
}
public Car Get(int id)
{
return _context.Cars.FirstOrDefault(x => x.Id == id);
}
public void Add(Car car)
{
_context.Cars.Add(car);
_context.SaveChanges();
}
}
}
Com essa estrutura básica criada, basta adicionar um novo projeto do tipo Unit Test no Visual Studio clicando em Solution > Add > New Project > Test > Unit Test Project.
Um ponto importante antes de começar a escrever os testes é lembrar-se de instalar o pacote Moq.
Install-Package Moq -Version 4.5.10

Moq (ou Mock) é um framework para realizar e configurar simulações em métodos e classes. A configuração de um objeto usando Moq é bem conveniente para se criar testes unitários e simular comportamentos que validem o funcionamento em determinados cenários.
Para inciar, vamos fazer um teste que chama o repositório e realiza a inclusão de um novo registro. Nesse exemplo vamos fazer o teste criando o método ShouldCreateNewCar(). Para isso, é preciso fazer um mock do DbSet do objeto, um mock do Context do Entity Framework e um mock da classe de repositório.
A notação consiste em instanciar um objeto Mock informando o tipo de objeto que deverá ser simulado. A linha de código fica da seguinte maneira: new Mock.
Logo abaixo temos um mock do nosso objeto DbSet.
var _dbSet = new Mock<DbSet<Car>>();
O próximo passo é fazer o mesmo com o Context do Entity Framework. Aqui entra o conceito de efetuar uma configuração (usando o método Setup) do nosso objeto mock para especificar um comportamento do mesmo. Neste caso, devemos configurá-lo para retornar um DbSet especifico ao chamar a nossa entidade de exemplo.
var _dbSet = new Mock<DbSet<Car>>();
var _context = new Mock<CarShoppingContext>();
_context.Setup(x => x.Cars).Returns(_dbSet.Object);
Com isso feito, usaremos os objetos mock como parâmetro na classe de repositório. Ela será instanciada no teste e receberá os objetos que criamos. Isso irá permitir testar a classe usando objetos de simulação que possuem o comportamento que configuramos anteriormente.
namespace LuizPauloPradoBlog.WebApi.Tests
{
[TestClass]
public class CarShoppingContextTestWithMock
{
[TestMethod]
public void ShouldCreateNewCar()
{
var _dbSet = new Mock<DbSet<Car>>();
var _context = new Mock<CarShoppingContext>();
_context.Setup(x => x.Cars).Returns(_dbSet.Object);
var _repository = new CarRepository(_context.Object);
var sampleCar = new Car() { Id = 3, Name = "Civic", Model = "Honda Civic SE", YearOfManufacture = 2016 };
_repository.Add(sampleCar);
_dbSet.Verify(m => m.Add(It.IsAny<Car>()), Times.Once());
_context.Verify(m => m.SaveChanges(), Times.Once());
}
}
}
Após isso, para validar o comportamento do que foi executado, usaremos o método Verify para confirmar que um objeto foi adicionado ao DbSet e também confirmar que o método de SaveChanges do Context foi devidamente acionado.
No segundo teste vamos chamar o repositório e realizar a listagem de alguns registros. Nesse exemplo vamos criar o método ShouldGetAllCars().
A preparação dos objetos mock seguem a mesma lógica do teste anterior. A diferença fica por conta de incluir registros de teste no DbSet e efetuar as configurações de um IQueryable que represente o nosso DbSet.
namespace LuizPauloPradoBlog.WebApi.Tests
{
[TestClass]
public class CarShoppingContextTestWithMock
{
[TestMethod]
public void ShouldGetAllCars()
{
var sampleData = new List<Car>()
{
new Car() { Id = 1, Name = "Fiesta", Model = "Fiesta SE", YearOfManufacture = 2015 },
new Car() { Id = 2, Name = "Golf", Model = "Golf Sport", YearOfManufacture = 2015 }
}.AsQueryable();
var _dbSet = new Mock<DbSet<Car>>();
_dbSet.As<IQueryable<Car>>().Setup(x => x.Provider).Returns(sampleData.Provider);
_dbSet.As<IQueryable<Car>>().Setup(x => x.Expression).Returns(sampleData.Expression);
_dbSet.As<IQueryable<Car>>().Setup(x => x.ElementType).Returns(sampleData.ElementType);
_dbSet.As<IQueryable<Car>>().Setup(x => x.GetEnumerator()).Returns(sampleData.GetEnumerator());
var _context = new Mock<CarShoppingContext>();
_context.Setup(x => x.Cars).Returns(_dbSet.Object);
var _repository = new CarRepository(_context.Object);
var cars = _repository.GetAll();
Assert.IsNotNull(cars);
Assert.IsTrue(cars.Any());
}
}
}
Neste caso, efetuamos a criação de uma lista de registros de exemplo como IQueryable e configuramos o mock do DbSet para utilizá-la. Ao chamar o método de GetAll() no repositório, o mesmo deve retornar exatamente os dados de exemplo que configuramos.
Por fim, podemos organizar melhor a configuração dos objetos de mock no método Initialize da classe de testes. O resultado ficou assim:
namespace LuizPauloPradoBlog.WebApi.Tests
{
[TestClass]
public class CarShoppingContextTestWithMock
{
private Mock<DbSet<Car>> _dbSet;
private Mock<CarShoppingContext> _context;
private ICarRepository _repository;
[TestInitialize]
public void Initialize()
{
var sampleData = new List<Car>()
{
new Car() { Id = 1, Name = "Fiesta", Model = "Fiesta SE", YearOfManufacture = 2015 },
new Car() { Id = 2, Name = "Golf", Model = "Golf Sport", YearOfManufacture = 2015 }
}.AsQueryable();
_dbSet = new Mock<DbSet<Car>>();
_dbSet.As<IQueryable<Car>>().Setup(x => x.Provider).Returns(sampleData.Provider);
_dbSet.As<IQueryable<Car>>().Setup(x => x.Expression).Returns(sampleData.Expression);
_dbSet.As<IQueryable<Car>>().Setup(x => x.ElementType).Returns(sampleData.ElementType);
_dbSet.As<IQueryable<Car>>().Setup(x => x.GetEnumerator()).Returns(sampleData.GetEnumerator());
_context = new Mock<CarShoppingContext>();
_context.Setup(x => x.Cars).Returns(_dbSet.Object);
_repository = new CarRepository(_context.Object);
}
[TestMethod]
public void ShouldCreateNewCar()
{
var sampleCar = new Car() { Id = 3, Name = "Civic", Model = "Honda Civic SE", YearOfManufacture = 2016 };
_repository.Add(sampleCar);
_dbSet.Verify(m => m.Add(It.IsAny<Car>()), Times.Once());
_context.Verify(m => m.SaveChanges(), Times.Once());
}
[TestMethod]
public void ShouldGetAllCars()
{
var cars = _repository.GetAll();
Assert.IsNotNull(cars);
Assert.IsTrue(cars.Any());
}
}
}
O projeto de exemplo pode ser encontrado no meu GitHub.
Pra quem quer pesquisar mais, o framework Moq possui muitas outras opções de métodos disponíveis, vale a pena conferir a documentação dele.
Se ficou alguma dúvida, comenta aqui no blog que eu tento ajudar. 🙂