SkillAgentSearch skills...

EntityFrameworkCore.Testing

Adds relational support to the Microsoft EntityFrameworkCore in-memory database provider by mocking relational operations.

Install / Use

/learn @rgvlee/EntityFrameworkCore.Testing
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

EntityFrameworkCore.Testing

Codacy Badge Codacy Badge

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

View on GitHub
GitHub Stars171
CategoryDevelopment
Updated1mo ago
Forks14

Languages

C#

Security Score

95/100

Audited on Jan 29, 2026

No findings