【问题标题】:Moq Entity Frameworks ExecuteSQLCommandMoq 实体框架 ExecuteSQLCommand
【发布时间】:2014-10-30 22:27:32
【问题描述】:

我已经读到,当使用 moq 时,您不能模拟非虚拟函数。我也读过这应该是可能的现在..这是真的吗? 如果是这样,那么我想模拟以下查询:

DatabaseContext.Database.ExecuteSqlCommand(updateQuery, newValue);

我在我的测试中覆盖了上下文

DAL.Context.DatabaseContext = mockContext.Object;

我已经尝试过这种设置,但查询仍然与我的常规数据库相反

mockContext.Setup(c => c.Set<AppSalesAndResult>()).Returns(mockBudgetData.Object);

任何想法,也许executesql命令可以用其他东西替换,以便上面的行可以捕捉到集合的任何更新?由于性能原因,我在一次更新多行时使用 executesqlcommand。普通 EF 太慢了

更新:

阅读下面的帖子,How to Moq Entity Framework SqlQuery calls 我想知道类似的实现是否适用于 ExecuteSQLCommand...

【问题讨论】:

  • 您能否发布您的问题的 MVP(最小可行原型)以便我们提供帮助?

标签: c# entity-framework moq


【解决方案1】:

为了能够模拟 ExecuteSqlCommand(在 DataBase 类中无法做到),我所做的是在我的 DbContext 继承中创建相同的方法,但这次是虚拟的,然后调用 Database.ExecuteSqlCommand

public class MyDbContext : DbContext
{
    public virtual int ExecuteSqlCommand(string sql, params object[] parameters)
    {
        return Database.ExecuteSqlCommand(sql, parameters);
    }
    public virtual int ExecuteSqlCommand(TransactionalBehavior transactionalBehavior, string sql, params object[] parameters)
    {
        return Database.ExecuteSqlCommand(transactionalBehavior, sql, parameters);
    }

然后我更改了我的业务代码以调用此创建的方法(不是数据库方法):

DatabaseContext.ExecuteSqlCommand(updateQuery, newValue);

然后就可以了

【讨论】:

  • 很好,必须对 CanConnect() 做同样的事情。
【解决方案2】:

可以模拟ExecuteSqlCommand。然而,这并不简单。

作为一种扩展方法,您必须模拟内部。它最终创建了一个RawSqlCommand 并在IRelationalCommand 上调用ExecuteNonQuery。随着扩展方法创建新对象来完成实际工作,并且没有RawSqlCommandDatabaseFacade 的接口,它变得更加复杂,你必须模拟具体的类。

我最终写了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 数据库属性上执行了设置作为模拟数据库上下文的一部分。我观察到内联新实例的测试成功,所以我认为这并不重要,就我而言,我不想启动另一个实例。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-03
    • 2012-10-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多