In this lesson, you will learn how to write reliable integration tests for your Repository pattern implementations to verify that your LINQ queries and data access logic behave correctly against a real database.
Why Mocking Isn't Enough
When working with the Repository pattern, we often use mocks for unit testing, but mocking DbSet or IQueryable is notoriously fragile. Mocks don't know how to translate your complex LINQ queries into SQL. If you accidentally write a query that performs an N+1 issue or uses an unsupported operator, your unit tests will pass, but your application will crash at runtime. Integration testing the repository ensures the translation between your C# code and the database engine is actually valid.
Testing the Repository
To test a repository, you need a live database instance. Using an in-memory provider (like InMemoryDatabase for EF Core) is often discouraged because it lacks the relational integrity and specific SQL behaviors of your production database. Instead, aim to use a real database (or Testcontainers) to execute the queries.
Consider a simple GetActiveUsersAsync method:
public async Task<List<User>> GetActiveUsersAsync()
=> await _context.Users.Where(u => u.IsActive).ToListAsync();
Your integration test should follow the Arrange-Act-Assert pattern:
- Arrange: Use your
DbContextto seed specific data into the database (some active users, some inactive). - Act: Call the repository method under test.
- Assert: Verify that the returned list contains only the active users, confirming the LINQ filter logic is correctly interpreted by the database.
[Fact]
public async Task GetActiveUsersAsync_ReturnsOnlyActiveUsers()
{
// Arrange: Seed database directly via context
_context.Users.AddRange(new User { IsActive = true }, new User { IsActive = false });
await _context.SaveChangesAsync();
// Act
var result = await _repository.GetActiveUsersAsync();
// Assert
Assert.Single(result);
Assert.True(result.All(u => u.IsActive));
}
By verifying the result against an actual database, you guarantee that your mapping, filtering, and asynchronous data retrieval work exactly as expected in the production environment.
Key Takeaway: Integration tests for repositories are essential because they validate that your LINQ-to-SQL logic executes correctly against the database engine, catching errors that mocks would otherwise ignore.