EntityFrameworkCore.Testing
Adds relational support to the Microsoft EntityFrameworkCore in-memory database provider by mocking relational operations.
Install / Use
/learn @rgvlee/EntityFrameworkCore.TestingREADME
EntityFrameworkCore.Testing
Overview
EntityFrameworkCore.Testing adds relational support to the Microsoft EntityFrameworkCore in-memory database provider by mocking relational operations. It's easy to use (usually just a single line of code) with implementations for both Moq and NSubstitute.
The aim of this library is to allow you use the in-memory database provider in unit tests where the SUT invokes a relational operation. It'll allow you to specify expected results for these relational operations. It does not test your relational operations.
Microsoft does not recommend mocking a db context and EntityFrameworkCore.Testing follows this advice by sending operations supported by the in-memory database provider to the in-memory database provider.
EntityFrameworkCore 10
EntityFrameworkCore 9
EntityFrameworkCore 8
EntityFrameworkCore 7
EntityFrameworkCore 6
EntityFrameworkCore 5
EntityFrameworkCore 3
EntityFrameworkCore 2 (2.1.0+)
Prerequisites
An accessible constructor
Your db context must have an accessible constructor.
Virtual sets/queries
Your db context set/query properties must be overridable:
public virtual DbSet<TestEntity> TestEntities { get; set; }
Creating a mocked DbContext
If your db context has an accessible constructor with a single DbContextOptions or DbContextOptions<TDbContext> parameter, creating a mocked db context is as easy as:
var mockedDbContext = Create.MockedDbContextFor<TestDbContext>();
Any accessible constructor can be used provided it has a DbContextOptions or DbContextOptions<TDbContext> parameter:
var mockedLogger = Mock.Of<ILogger<TestDbContext>>();
var dbContextOptions = new DbContextOptionsBuilder<TestDbContext>().UseInMemoryDatabase(Guid.NewGuid().ToString()).Options;
var mockedDbContext = Create.MockedDbContextFor<TestDbContext>(mockedLogger, dbContextOptions);
Both of the above examples automatically create and use a Microsoft in-memory provider instance for the EntityFrameworkCore provider. If you want more control e.g., to specify the EntityFrameworkCore provider instance, use the builder:
var options = new DbContextOptionsBuilder<TestDbContext>().UseInMemoryDatabase(Guid.NewGuid().ToString()).Options;
var dbContextToMock = new TestDbContext(options);
var mockedDbContext = new MockedDbContextBuilder<TestDbContext>().UseDbContext(dbContextToMock).UseConstructorWithParameters(options).MockedDbContext;
There is no requirement to use the Microsoft in-memory provider. The following example uses the SQLite in-memory provider for a db context with a parameterless constructor:
using (var connection = new SqliteConnection("Filename=:memory:"))
{
connection.Open();
var testEntity = _fixture.Create<TestEntity>();
var dbContextToMock = new TestDbContext(new DbContextOptionsBuilder<TestDbContext>().UseSqlite(connection).Options);
dbContextToMock.Database.EnsureCreated();
var mockedDbContext = new MockedDbContextBuilder<TestDbContext>().UseDbContext(dbContextToMock).MockedDbContext;
mockedDbContext.Set<TestEntity>().Add(testEntity);
mockedDbContext.SaveChanges();
Assert.Multiple(() =>
{
Assert.AreNotEqual(default(Guid), testEntity.Id);
Assert.DoesNotThrow(() => mockedDbContext.Set<TestEntity>().Single());
Assert.AreEqual(testEntity, mockedDbContext.Find<TestEntity>(testEntity.Id));
});
}
Usage
Start by creating a mocked db context and, if the SUT requires, populate it as if you were using the real thing:
var testEntity = _fixture.Create<TestEntity>();
var mockedDbContext = Create.MockedDbContextFor<TestDbContext>();
mockedDbContext.Set<TestEntity>().Add(testEntity);
mockedDbContext.SaveChanges();
Assert.Multiple(() =>
{
Assert.AreNotEqual(default(Guid), testEntity.Id);
Assert.DoesNotThrow(() => mockedDbContext.Set<TestEntity>().Single());
Assert.AreEqual(testEntity, mockedDbContext.Find<TestEntity>(testEntity.Id));
});
The Moq implementation of Create.MockedDbContextFor<T>() returns the mocked db context. If you need the mock itself (e.g., to verify an invocation) use Mock.Get(mockedDbSet):
var mockedDbContext = Create.MockedDbContextFor<TestDbContext>();
mockedDbContext.Set<TestEntity>().AddRange(_fixture.CreateMany<TestEntity>().ToList());
mockedDbContext.SaveChanges();
Assert.Multiple(() =>
{
var dbContextMock = Mock.Get(mockedDbContext);
dbContextMock.Verify(m => m.SaveChanges(), Times.Once);
});
FromSql
Use AddFromSqlResult to add a from SQL result to the mock. The following will return expectedResult for any FromSql<TestEntity> invocation:
var expectedResult = _fixture.CreateMany<TestEntity>().ToList();
var mockedDbContext = Create.MockedDbContextFor<TestDbContext>();
mockedDbContext.Set<TestEntity>().AddFromSqlRawResult(expectedResult);
var actualResult = mockedDbContext.Set<TestEntity>().FromSqlRaw("[dbo].[USP_StoredProcedureWithNoParameters]").ToList();
Assert.Multiple(() =>
{
Assert.IsNotNull(actualResult);
Assert.IsTrue(actualResult.Any());
CollectionAssert.AreEquivalent(expectedResult, actualResult);
});
The following will return expectedResult if the FromSql SQL query text contains usp_StoredProcedureWithParameters and a @Parameter2 SQL parameter with a value of Value2 has been provided:
var expectedResult = _fixture.CreateMany<TestEntity>().ToList();
var sqlParameters = new List<SqlParameter> { new SqlParameter("@Parameter2", "Value2") };
var mockedDbContext = Create.MockedDbContextFor<TestDbContext>();
mockedDbContext.Set<TestEntity>().AddFromSqlRawResult("usp_StoredProcedureWithParameters", sqlParameters, expectedResult);
var actualResult = mockedDbContext.Set<TestEntity>()
.FromSqlRaw("[dbo].[USP_StoredProcedureWithParameters] @Parameter1 @Parameter2",
new SqlParameter("@parameter1", "Value1"),
new SqlParameter("@parameter2", "value2"))
.ToList();
Assert.Multiple(() =>
{
Assert.IsNotNull(actualResult);
Assert.IsTrue(actualResult.Any());
CollectionAssert.AreEquivalent(expectedResult, actualResult);
});
SQL query text matching supports partial, case insensitive matches. Individual parameter name and value matching is also case insensitive. Case insensitive interpolated strings are also supported:
var expectedResult = _fixture.CreateMany<TestEntity>().ToList();
var parameter1 = _fixture.Create<DateTime>();
var parameter2 = _fixture.Create<string>();
var mockedDbContext = Create.MockedDbContextFor<TestDbContext>();
mockedDbContext.Set<TestEntity>().AddFromSqlInterpolatedResult($"usp_StoredProcedureWithParameters {parameter1}, {parameter2.ToUpper()}", expectedRe
Related Skills
node-connect
339.3kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
83.9kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
339.3kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
83.9kCommit, push, and open a PR
