I'm new to moq and setting up mocks so i could do with a little help. How do I mock up an SqlDataReader using Moq?
Update
After further testing this is what I have so far:
private IDataReader MockIDataReader()
{
var moq = new Mock<IDataReader>();
moq.Setup( x => x.Read() ).Returns( true );
moq.Setup( x => x.Read() ).Returns( false );
moq.SetupGet<object>( x => x["Char"] ).Returns( 'C' );
return moq.Object;
}
private class TestData
{
public char ValidChar { get; set; }
}
private TestData GetTestData()
{
var testData = new TestData();
using ( var reader = MockIDataReader() )
{
while ( reader.Read() )
{
testData = new TestData
{
ValidChar = reader.GetChar( "Char" ).Value
};
}
}
return testData;
}
The issue you is when I do reader.Read in my GetTestData() method its always empty. I need to know how to do something like
reader.Stub( x => x.Read() ).Repeat.Once().Return( true )
as per the rhino mock example: Mocking a DataReader and getting a Rhino.Mocks.Exceptions.ExpectationViolationException: IDisposable.Dispose(); Expected #0, Actual #1
Moq has an ability to run some code after the method is executed. It is called "Callback". Modify your code this way and it will work:
But what if it will be required IDataReader to contain not only single row, but several? Well, here is a sample:
This does not let you mock a
SqlDataReader
but if your function is returning aDbDataReader
(The base class ofSqlDataReader
) or aIDataReader
the easist way to mock it is just use aDataTable
or aDataSet
and call itsCreateDataReader()
function and return that.First, in a separate project, run your query like normal to produce some test data and use the
WriteXmlSchema
to generate a .xsd file and theWriteXml
functions to hold the test data.In your test project add
TestDataSet.xsd
to the project and make sure it has the custom tool ofMSDataSetGenerator
(it should have it by default). This will cause aDataTable
derived class namedTestDataSet
to be generated that has the schema of your query.Then add
TestDataSetData.xml
as a resource to your test project. Finally in your test create theTestDataSet
and callReadXml
using the text from the xml file you generated.This will create a data reader that will act just like the data reader that would have been returned from the sql query and even supports things like multiple result sets returned.
I was just trying to figure this out myself. Not sure if this is new functionality in Moq, but it appears there is a simpler way than @Monsignor's answer.
Use Moq's
SetupSequence
method. Your code simply becomes:After some testing the problem is trying to set the DataReader.Read() to true for one loop and then setting it to false. Rhino Mock has the Repeat.Once() option but I could not find a similar method in Moq (I might be wrong here).
The main reason for testing this was the extension methods to convert the reader to the relevant datatype so in the end I removed the while loop and just accessed the values that had been setup in my mock. The code looks as follows:
Not an ideal solution but it works. If anyone knows better leave a comment thanks.
Inspired by @mikesigs answer and another question: SetupSequence in Moq I've came up with the following extension method that does the work for you:
And the usage example: