简体   繁体   English

System.Not Supported 用于模拟 ExecuteSQLCommand Moq Entity Framework Core 的异常

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

I am trying to write a unit test for a method in my database layer.我正在尝试为我的数据库层中的方法编写单元测试。 I am getting error at context.Setup for ExecuteSqlCommand :我在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. System.NotSupportedException: '不支持的表达式:... => .... ExecuteSqlCommand((RawSqlString)It.IsAny<string>(), It.IsAny<object[]>())扩展方法(此处:RelationalDatabaseFacadeExtensions.ExecuteSqlCommand ) 不能用于设置/验证表达式。

My method looks like this:我的方法是这样的:

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

My unit test is something like this:我的单元测试是这样的:

[Fact]
[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);
        repo.InsertIntoQueue();                
    }
}

Is there a way to mock the ExecuteSQLCommand method directly?有没有办法直接模拟ExecuteSQLCommand方法?

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.我有一个类似的案例,最终实现了EntityFrameworkCore.Testing,因为它是一个非常复杂的 Moq 设置。

var relationalCommandMock = new Mock<IRelationalCommand>();
relationalCommandMock
    .Setup(m => m.ExecuteNonQuery(It.IsAny<IRelationalConnection>(), It.IsAny<IReadOnlyDictionary<string, object>>()))
    .Returns((IRelationalConnection providedConnection, IReadOnlyDictionary<string, object> providedParameterValues) => executeSqlCommandResult);
relationalCommandMock
    .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>();

rawSqlCommandBuilderMock
    .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.这是支持 ExecuteSqlCommand 和 ExecuteSqlCommandAsync 所需的设置。 executeSqlCommandResult is the integer result to return when invoked. executeSqlCommandResult是调用时要返回的整数结果。

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.请注意,我将模拟的 db 上下文传递给数据库外观模拟构造函数,然后在模拟数据库上执行另一个 Moq 设置,我没有这样做,因为我已经在 DbContext 数据库属性上执行了设置作为模拟数据库的一部分语境。 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.我已经观察到内联新实例的测试成功,所以我认为这并不重要,就我而言,我不想启动另一个实例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM