chevron-thin-right chevron-thin-left brand cancel-circle search youtube-icon google-plus-icon linkedin-icon facebook-icon twitter-icon toolbox download check linkedin phone twitter-old google-plus facebook profile-male chat calendar profile-male
0 votes
The example code that follows throws the following exception;
System.InvalidOperationException : Invalid attempt to read when no data is present.

What is the correct way to Mock this?

using System;
using System.Data.SqlClient;
using System.Data;
using TypeMock;
using NUnit.Framework;

namespace People
{
    public class Person
    {
        private int _Id = -1;
        public string Name = string.Empty;

        private Person(int Id) { DataFetch(Id); }
        
        public static Person GetPerson(int Id) { return new Person(Id); }

        private void DataFetch(int Id)
        {
            using (SqlConnection Cn = new SqlConnection("ConnectionString"))
            {
                Cn.Open();
               using (SqlCommand Cm = Cn.CreateCommand())
                {
                    Cm.CommandType = CommandType.StoredProcedure;
                    Cm.CommandText = "getPerson";
                    Cm.Parameters.AddWithValue("@Id", Id);
                    using (SqlDataReader dr = Cm.ExecuteReader())
                    {
                        _Id = Id;
                        Name = dr.GetString(1);
                    }
                }
            }
        }
    }

    [TestFixture]
    public class PersonTestFixture
    {
        [Test]
        public void TestGettingAPerson()
        {
            const int PERSON_ID = 1;
            const string PERSON_NAME = "John Smith";

            Mock MockSqlConnection = MockManager.MockAll(typeof(SqlConnection));
            Mock MockSqlCommand = MockManager.MockAll(typeof(SqlCommand));
            Mock MockSqlParameters = MockManager.MockAll(typeof(SqlParameterCollection), Constructor.NotMocked);
            Mock MockSqlDataReader = MockManager.MockAll(typeof(SqlDataReader), Constructor.Mocked);

            SqlDataReader dr = (SqlDataReader)MockManager.MockObject(typeof(SqlDataReader), Constructor.Mocked, null, null).Object;

            MockSqlConnection.ExpectCall("Open", 1);
            MockSqlConnection.ExpectCall("Close", 1);
            MockSqlParameters.ExpectUnmockedCall("AddWithValue", 1).Args("@Id", PERSON_ID);
            MockSqlCommand.ExpectAndReturn("ExecuteReader", dr, 1);
            MockSqlDataReader.ExpectAndReturn("GetString", PERSON_NAME, 1).Args(1);

            Person JohnSmith = Person.GetPerson(PERSON_ID);
            Assert.AreEqual(PERSON_NAME, JohnSmith.Name);
        }
    }
}
asked by BjP (1.1k points)

1 Answer

0 votes
Hi,
The only thing you have to do is change your MockObject Lines to:
MockObject MockSqlDataReader = MockManager.MockObject(typeof(SqlDataReader), Constructor.Mocked);

SqlDataReader dr = (SqlDataReader)MockSqlDataReader.Object;


This way you will be setting expectations on the MockObject that is returned by MockSqlCommand.ExecuteReader (The code you showed mock all other instances of SqlCommand, not the one returned 8) )

I found this by using the Tracer, although you need a license to see more then 1 type. Of course as your mocks have multiple chained objects I would use Natural Mocks to make the tests understandable.

[Test]
public void TestGettingAPerson()
{
    const int PERSON_ID = 1;
    const string PERSON_NAME = "John Smith";

    using (RecordExpectations rec = RecorderManager.StartRecording())
    {
        SqlConnection Cn = new SqlConnection("what-ever");
        rec.MockStaticConstructors();
        Cn.Open();
        Cn.Close();

        SqlCommand Cm = new SqlCommand();
        Cm.Parameters.AddWithValue("@Id", PERSON_ID);
        rec.CheckArguments();

        SqlDataReader dr = Cm.ExecuteReader();

        dr.GetString(1);
        rec.Return(PERSON_NAME).CheckArguments();
    }

    Person JohnSmith = Person.GetPerson(PERSON_ID);
    Assert.AreEqual(PERSON_NAME, JohnSmith.Name);
}
answered by scott (32k points)
...