How to mock SqlParameterCollection using Moq
Asked Answered
A

4

8

I am trying to mock database operations. I have problem in mocking SqlParameterCollection. I tried to create virtual method that will return DbParameterCollection but then i am loosing all the functionality that SqlParameterCollection gives like AddWithValue etc. Is there a way i can mock SqlParameterCollection? Is there any other approach to unit test DAL? I am using Moq.

Code goes like this:

in 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();
           }
     }
}

In Unit test project:

    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());
    }

--Solution--

Created an extension method to add parameter with value. Thank you Marc Gravell for pointing me to that direction.

    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;

    }
Albinus answered 16/6, 2011 at 18:23 Comment(1)
You might want to call .Parameters.Add with the newly created parameter, since that is expected with an Add methodElegance
E
9

Personally, I approach this problem by writing an AddParameterWithValue extension method to DbCommand (or IDbCommand). It has to be on the command so that you have access to CreateParameter, and then call .Parameters.Add.

This allows easy usage against any ADO.NET stack, including abstractions like logging decorators.

Elegance answered 16/6, 2011 at 18:28 Comment(2)
@Albinus I think I left a version of this in "dapper" if you need itElegance
That will be cool. I am writing the extension method but struggling there.Albinus
R
2

@Asdfg HI I have basically mocked the parameter collection as below

 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;
        //*************************************************//

Hope this helps

Retrogress answered 2/2, 2015 at 12:44 Comment(0)
L
2

Hi i found the solution.

I had to implement a Moq for the IDataParameterCollection interface and had to send it to the instance of IDbCommand.

With that my IDbCommand.Parameters object became different from 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;
    }
Laomedon answered 5/4, 2020 at 23:5 Comment(1)
What is DbConnectionMock_Success in your code here?Greathouse
R
0

If you don't want to add extension method just to make your code testable (as proposed in another answer to this question) you can easily mock your way around this like so:

var dbCommandMock = new Mock<DbCommand>();
dbCommandMock.Protected()
   .Setup<DbParameterCollection>("DbParameterCollection")
   .Returns(new Mock<DbParameterCollection>().Object);
Roderickroderigo answered 5/6, 2023 at 9:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.