如何使用Moq模拟SqlParameterCollection

8
我是一名有用的助手,可以翻译文本。
我正在尝试模拟数据库操作。我在模拟SqlParameterCollection时遇到了问题。我尝试创建一个虚拟方法来返回DbParameterCollection,但是这样我就失去了SqlParameterCollection提供的所有功能,例如AddWithValue等。有没有办法模拟SqlParameterCollection?是否有其他方法来单元测试DAL?我正在使用Moq。
代码如下:
在DAL中:
protected virtual IDbConnection GetConnection(string connectionString)
{
    return new SqlConnection(connectionString);
}

protected virtual IDbCommand GetCommand(IDbConnection cn)
{
    return cn.CreateCommand();
}

protected virtual IDbTransaction GetTransaction(IDbConnection cn)
{
    return cn.BeginTransaction(IsolationLevel.Serializable);
}

Public Bool InsertInDatabase(DataTable dt)
{
   using (IDbConnection cn = GetConnection(cnstr))
      {
         cn.Open();

            using (IDbTransaction tran = GetTransaction(cn))
            {
                IDbCommand cmd = GetCommand(cn);
                cmd.Transaction = tran;
                cmd.Connection = cn;
                cmd.CommandType = CommandType.StoredProcedure;

                cmd.CommandText = "sp_InsertInDatabase";
                SqlParameterCollection cmdParams = cmd.Parameters as SqlParameterCollection;
                cmdParams.AddWithValue("@param1", dt);
                cmd.ExecuteNonQuery();
           }
     }
}

在单元测试项目中:

    protected override IDbConnection GetConnection(string connectionString)
    {
        return Mock.Of<IDbConnection>();
    }

    protected override IDbCommand GetCommand(IDbConnection cn)
    {
        return Mock.Of<IDbCommand>();
    }

    protected override IDbTransaction GetTransaction(IDbConnection cn)
    {
        return Mock.Of<IDbTransaction>();
    }

    public void TestInsertInDatabase()
    {
        base.InsertInDatabase(new DataTable());
    }

--解决方法--

创建了一个扩展方法来添加带有值的参数。感谢 Marc Gravell 指引我朝这个方向。

    public static IDbDataParameter AddParameterWithValue(this IDbCommand cmd, string paramName, object paramValue)
    {

        var dbParam = cmd.CreateParameter();
        if (dbParam != null)
        {
            dbParam.ParameterName = paramName;
            dbParam.Value = paramValue;
        }
        return dbParam;

    }

你可能想使用新创建的参数调用.Parameters.Add,因为这是Add方法所期望的。 - Marc Gravell
4个回答

9
个人而言,我解决这个问题的方法是编写一个 AddParameterWithValue 扩展方法到 DbCommand(或 IDbCommand)中。它必须在命令上,以便您可以访问 CreateParameter,然后调用 .Parameters.Add
这使得它可以轻松地使用任何 ADO.NET 堆栈,包括像日志修饰符这样的抽象层。

@Asdfg 如果你需要,我认为我在“dapper”中留下了一个版本。 - Marc Gravell
那会很酷。我正在编写扩展方法,但遇到了困难。 - Asdfg

2

@Asdfg 你好,我基本上已经如下模拟了参数集合

 string connectionString = "connectionstring";
        var sqlConnection = new SqlConnection(connectionString);
        var command = sqlConnection.CreateCommand();
        //****************Setup Mock************************//
        Castle.DynamicProxy.Generators.AttributesToAvoidReplicating.Add(typeof(System.Data.SqlClient.SqlClientPermissionAttribute));
        var mockDataReader1 = new Mock<IDataReader>();
        command.Parameters.Add(new SqlParameter("@po_tint_Result", 1));
        //setup read return value
        Queue<bool> responseQueue = new Queue<bool>();
        responseQueue.Enqueue(true);
        responseQueue.Enqueue(false);
        mockDataReader1.Setup(a => a.Read()).Returns(() => responseQueue.Dequeue());
        var mockDb = new Mock<SqlDatabase>(connectionString);
        mockDb.Setup(a => a.GetStoredProcCommand("SPNAME")).Returns(command);
        mockDb.Setup(a => a.ExecuteNonQuery(command));
        obj1.DbConn = mockDb.Object;
        //*************************************************//

希望这能帮到你。

2

您好,我找到了解决方案。

我需要为IDataParameterCollection接口实现Moq,并将其发送到IDbCommand实例。

这样我的IDbCommand.Parameters对象就不再是null了。

    public static IDbConnection IDbConnectionMock(int valReturn)
    {
        var dataParameterCollection = new Mock<IDataParameterCollection>();

        var command = new Mock<IDbCommand>();
        command.Setup(x => x.Parameters).Returns(dataParameterCollection.Object);
        command.Setup(x => x.ExecuteNonQuery()).Returns(valReturn);

        var connection = DbConnectionMock_Success(command.Object);

        return connection;
    }

你的代码中的DbConnectionMock_Success是什么? - Patrick Szalapski

0
如果你不想为了让你的代码可测试而添加扩展方法(如在另一个回答中提到的),你可以轻松地通过模拟来解决这个问题,就像这样:
var dbCommandMock = new Mock<DbCommand>();
dbCommandMock.Protected()
   .Setup<DbParameterCollection>("DbParameterCollection")
   .Returns(new Mock<DbParameterCollection>().Object);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接