Unit Testing with Mocked IDataReader Using Moq

Mock IDataReader using Moq to unit test a class that uses IDataReader

Posted by Alfus Jaganathan on Thursday, September 16, 2021

Background

Recently, I came across a situation when writing unit tests while building a Repository class, where I was using IDataReader to read the records from database. The challenge comes in, when trying to mock IDataReader to return the rows as it if the rows are returned from database.

When I started mocking IDataReader, I felt it be more complex (than I originally thought) to cover all scenarios (e.g. multiple data types, nullable and non-nullable columns, etc.). I couldn’t find a good solution over in internet either, appears that, it still remain unanswered in several websites like stack overflow.

If you are practicing TDD when building .NET applications, the chances are more likely that you came across this.

So, I started writing some helper mock which can solve the problem easily, which can also save a lot of time in writing customized mocks.

Later I thought of sharing it as a nuget package for anyone who is in need of it. Below are some of the highlighting features of the package.

  • Works with Moq
  • Supports most of the data types (string, int, short, long, float, decimal, double, guid, datetime, bool, char and byte)

And now, let’s look into the usage instructions, which shows how cool and easy it is. You can also refer to the instructions at project site.

Usage Instructions

First, we need to install latest package of MoqExtensions.DataReader

Our initial goal is to build the data, that the reader is supposed to provide (as what it is supposed to, when pulling from a database)

For that, we need to create a DataTable as below specifying the data and schema as supposed to be returned by the sql query used by IDataReader

    var mockDataTable = new MockDataTable();

    //Add Columns (schema)
    mockDataTable.Columns.Add(new MockDataColumn("Column1", typeof(string)));
    mockDataTable.Columns.Add(new MockDataColumn("Column2", typeof(string)));
    mockDataTable.Columns.Add(new MockDataColumn("Column3", typeof(string), true));

    //Add Rows (data) - column order is important here
    mockDataTable.Rows.Add(new MockDataRow("Row1_Column1_Value", "Row1_Column2_Value", "Row1_Column3_Value");
    mockDataTable.Rows.Add(new MockDataRow("Row2_Column1_Value", "Row2_Column2_Value", "Row2_Column3_Value");
    mockDataTable.Rows.Add(new MockDataRow("Row3_Column1_Value", "Row3_Column2_Value", "Row3_Column3_Value");
    mockDataTable.Rows.Add(new MockDataRow("Row4_Column1_Value", "Row4_Column2_Value", "Row4_Column3_Value");

Now that we have the mock data and schema ready, we can create a mock datareader as below using Moq and setup to return the mocked data, using the extension method SetupWithReturn

    var reader = new Mock<IDataReader>();

    //Setup reader to return the data from datatable
    reader.SetupWithReturn(table);

At this point, we are all set to use our mocked IDataReader for unit testing a Repository or any class that uses it. Sample implementation relating to the above, is given below.

    while (reader.Read())
    {
        models.Add(new Model
        {
            Column1 = reader.GetString(reader.GetOrdinal("Column1")),
            Column2 = reader.GetString(reader.GetOrdinal("Column2")),
            Column3 = Convert.IsDBNull(reader.GetOrdinal("Column3")) ? null : reader.GetString(reader.GetOrdinal("Column3"))
        });
    }   

Hope you enjoyed using it!

Source Code Repository

Github

Love helping the community!


comments powered by Disqus