System.Not Supported exception for mocking ExecuteSQLCommand Moq Entity Framework Core

c# entity-framework-core moq unit-testing


I am trying to write a unit test for a method in my database layer. I am getting error at context.Setup for ExecuteSqlCommand:

System.NotSupportedException: 'Unsupported expression: ... => ....ExecuteSqlCommand((RawSqlString)It.IsAny<string>(), It.IsAny<object[]>()) Extension methods (here: RelationalDatabaseFacadeExtensions.ExecuteSqlCommand) may not be used in setup / verification expressions.

My method looks like this:

public void InsertIntoQueue()
           .ExecuteSqlCommand(" EXEC SP_Name param1",
                              new SqlParameter("param1", param1.value));           

My unit test is something like this:

[Trait("TestCategory", "Unit")]
public async void Test1_Ok()
    var context = new Mock<Context>();
        context.Setup(x => x.Database.ExecuteSqlCommand(It.IsAny<string>(), It.IsAny<object[]>())).Returns(1);
        var repo = GetDBRepository(context.Object);

Is there a way to mock the ExecuteSQLCommand method directly?

11/14/2019 5:46:04 PM

Popular Answer

It is not possible to mock it directly as it is an extension method.

It is possible to mock what that extension method invokes though and mock it indirectly. I had a similar case and ended up implementing EntityFrameworkCore.Testing as it's a pretty involved Moq set up.

var relationalCommandMock = new Mock<IRelationalCommand>();
    .Setup(m => m.ExecuteNonQuery(It.IsAny<IRelationalConnection>(), It.IsAny<IReadOnlyDictionary<string, object>>()))
    .Returns((IRelationalConnection providedConnection, IReadOnlyDictionary<string, object> providedParameterValues) => executeSqlCommandResult);
    .Setup(m => m.ExecuteNonQueryAsync(It.IsAny<IRelationalConnection>(), It.IsAny<IReadOnlyDictionary<string, object>>(), It.IsAny<CancellationToken>()))
    .Returns((IRelationalConnection providedConnection, IReadOnlyDictionary<string, object> providedParameterValues, CancellationToken providedCancellationToken) => Task.FromResult(executeSqlCommandResult));
var relationalCommand = relationalCommandMock.Object;

var rawSqlCommandMock = new Mock<RawSqlCommand>(MockBehavior.Strict, relationalCommand, new Dictionary<string, object>());
rawSqlCommandMock.Setup(m => m.RelationalCommand).Returns(relationalCommand);
rawSqlCommandMock.Setup(m => m.ParameterValues).Returns(new Dictionary<string, object>());
var rawSqlCommand = rawSqlCommandMock.Object;

var rawSqlCommandBuilderMock = new Mock<IRawSqlCommandBuilder>();

    .Setup(m => m.Build(It.IsAny<string>(), It.IsAny<IEnumerable<object>>()))
    .Returns((string providedSql, IEnumerable<object> providedParameters) => rawSqlCommand);

var rawSqlCommandBuilder = rawSqlCommandBuilderMock.Object;

var serviceProviderMock = new Mock<IServiceProvider>();
serviceProviderMock.Setup(m => m.GetService(It.Is<Type>(t => t == typeof(IConcurrencyDetector)))).Returns((Type providedType) => Mock.Of<IConcurrencyDetector>());
serviceProviderMock.Setup(m => m.GetService(It.Is<Type>(t => t == typeof(IRawSqlCommandBuilder)))).Returns((Type providedType) => rawSqlCommandBuilder);
serviceProviderMock.Setup(m => m.GetService(It.Is<Type>(t => t == typeof(IRelationalConnection)))).Returns((Type providedType) => Mock.Of<IRelationalConnection>());
var serviceProvider = serviceProviderMock.Object;

var databaseFacadeMock = new Mock<DatabaseFacade>(MockBehavior.Strict, mockedDbContext);
databaseFacadeMock.As<IInfrastructure<IServiceProvider>>().Setup(m => m.Instance).Returns(serviceProvider);
var databaseFacade = databaseFacadeMock.Object;

Mock.Get(mockedDbContext).Setup(m => m.Database).Returns(databaseFacade);

This is the set up required to support both ExecuteSqlCommand and ExecuteSqlCommandAsync. executeSqlCommandResult is the integer result to return when invoked.

Note that I am passing the mocked db context to the database facade mock constructor then performing another Moq set up on the mocked database, I get away with this as I have already performed a set up on the DbContext Database property as part of mocking the db context. I have observed test success with an inline new instance so I don't think it matters, just in my case I didn't want to spin up another instance.

11/14/2019 11:44:09 PM

Related Questions


Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow