Testing Entity Framework: Best Practices & Examples
Entity Framework (EF) is a powerful ORM that abstracts database access. Proper testing ensures your data layer behaves as expected without requiring an actual database during unit tests.
Why Test EF Code?
- Detect regressions early.
- Validate LINQ queries translate correctly.
- Ensure business rules embedded in the model work.
Testing Strategies
1. In‑Memory Provider
EF Core ships with an InMemory provider that mimics a database in memory. It’s fast and perfect for unit tests.
using Microsoft.EntityFrameworkCore;
using Xunit;
public class BloggingContextTests
{
private DbContextOptions<BloggingContext> Options =>
new DbContextOptionsBuilder<BloggingContext>()
.UseInMemoryDatabase(databaseName: "BlogTestDb")
.Options;
[Fact]
public void CanAddBlog()
{
using var context = new BloggingContext(Options);
context.Blogs.Add(new Blog { Url = "https://example.com" });
context.SaveChanges();
Assert.Equal(1, context.Blogs.Count());
}
}
2. SQLite In‑Memory
For more realistic relational behavior, use SQLite's in‑memory mode.
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Xunit;
public class BloggingContextSQLiteTests
{
[Fact]
public void CanQueryWithRelationalFeatures()
{
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
var options = new DbContextOptionsBuilder<BloggingContext>()
.UseSqlite(connection)
.Options;
using (var context = new BloggingContext(options))
{
context.Database.EnsureCreated();
context.Blogs.Add(new Blog { Url = "https://example.com" });
context.SaveChanges();
}
using (var context = new BloggingContext(options))
{
var blog = context.Blogs.First();
Assert.Equal("https://example.com", blog.Url);
}
}
}
3. Mocking DbSet
When you need to isolate repository logic, mock DbSet<T> with a framework like Moq.
using Moq;
using Microsoft.EntityFrameworkCore;
using Xunit;
public class BlogRepositoryTests
{
[Fact]
public void GetAll_ReturnsAllBlogs()
{
var data = new List<Blog>
{
new Blog { Id = 1, Url = "https://a.com" },
new Blog { Id = 2, Url = "https://b.com" }
}.AsQueryable();
var mockSet = new Mock<DbSet<Blog>>();
mockSet.As<IQueryable<Blog>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<Blog>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<Blog>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<Blog>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
var mockContext = new Mock<BloggingContext>();
mockContext.Setup(c => c.Blogs).Returns(mockSet.Object);
var repo = new BlogRepository(mockContext.Object);
var result = repo.GetAll();
Assert.Equal(2, result.Count());
}
}
Common Pitfalls
- Using
InMemoryfor relational‑specific queries (e.g.,LIKEoperations) can produce false positives. - Not resetting the in‑memory database between tests leads to state leakage.
- Over‑mocking can hide real EF behavior; prefer in‑memory providers where possible.
Happy testing! 🎉