可以模拟ExecuteSqlCommand。然而,这并不简单。
作为一种扩展方法,您必须模拟内部。它最终创建了一个RawSqlCommand 并在IRelationalCommand 上调用ExecuteNonQuery。随着扩展方法创建新对象来完成实际工作,并且没有RawSqlCommand 或DatabaseFacade 的接口,它变得更加复杂,你必须模拟具体的类。
我最终写了EntityFrameworkCore.Testing,因为周围没有其他东西可以做所有的模拟(FromSql、ExecuteSqlCommand、DbQuery,内存中提供程序不能做的关系的东西)。为自己节省一些时间,因为涉及到模拟,如果我正在寻找一个现有的包,我当然会使用现有的包。
如果您确实想自己动手,ExecuteSqlCommand/ExecuteSqlCommandAsync 的模拟设置如下所示:
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);
executeSqlCommandResult 是期望返回的整数。您可以使用回调来应用上述 sql 命令对您的数据源执行的任何操作。
请注意,我将模拟的 db 上下文传递给数据库外观模拟构造函数,然后在模拟数据库上执行另一个 Moq 设置,因为我已经在 DbContext 数据库属性上执行了设置作为模拟数据库上下文的一部分。我观察到内联新实例的测试成功,所以我认为这并不重要,就我而言,我不想启动另一个实例。