Wednesday, November 19, 2008

Using NMock to Create an IDataReader for Testing Data Access Methods

If you have a simple Data Access class with a method like the one below which you need to test without depending on specific values in the database (and without embarking on a major refactoring expedition).

public decimal GetAvailableBalance(int custNumb)
{
decimal retVal = 0.00M; 
using (SqlConnection sqlConnection = new SqlConnection(_dbConnection))
{
sqlConnection.Open();
using (SqlCommand sqlCommand = sqlConnection.CreateCommand())
{
sqlCommand.CommandType = CommandType.StoredProcedure;
sqlCommand.CommandText = "dbo.GetCustomerBalance";
sqlCommand.Parameters.AddWithValue("CustomerID", custNumb);
using (IDataReader dr = sqlCommand.ExecuteReader())
{
if (dr != null)
{
dr.Read();
retVal = Convert.ToDecimal(dr["AvailableBalance"]);
}
}
}
}
return retVal; 
}


Replace this:
sqlCommand.ExecuteReader()



With This:
public virtual IDataReader GetReader(SqlCommand cmd)
{
return cmd.ExecuteReader(); 
}



Now create a test class that inherits from the Data Access class and override that virtual method. Like so:

[TestFixture]
public class IMTTests : TransactionStore
{
Mockery mocks = new Mockery(); 
IDataReader idr = null; 

public override IDataReader GetReader(SqlCommand cmd)
{
Expect.AtLeastOnce.On(idr).Method("Read").Will(Return.Value(true));
Expect.AtLeastOnce.On(idr).Method("Dispose"); 
return idr; 
}



Now in your test, create your mock DataReader to assign to the private variable and set the specific expectations of your data mapping.

[Test]
public void TestAvailableBalance()
{
idr = (IDataReader)mocks.NewMock(typeof(IDataReader));
Expect.AtLeastOnce.On(idr).Get["AvailableBalance"].Will(Return.Value(42.00M));
Decimal amount = this.GetAvailableBalance(12345);
Assert.AreEqual(42.00M, amount); 
}

1 comment:

  1. Thanks bro.. exactly what this mock newbie was looking for!

    one question though! Why does it now work when I use Once instead of AtLeastOnce like

    Expect.Once.On(idr).Get["Field1"].Will(Return.Value(1));
    Expect.Once.On(idr).Get["Field2"].Will(Return.Value(1));

    ReplyDelete