Search code examples
unit-testingnhibernatemockingrhino-mocks

mock datareader failing on second call


In the test below, the mocked datareader returns the desired value the first time, but then returns the same value when the index should be 1.

Am I misusing the dataReader or Rhino stub syntax? What is the fix?

Cheers,
Berryl

failing test

[Test]
public void NullSafeGet_GetsBothProperties()
{
    var sessionImplementor = MockRepository.GenerateStub<ISessionImplementor>();
    var userType = new DateRangeUserType();

    var reader = MockRepository.GenerateStub<IDataReader>();           
    var start = new DateTime(2011, 6, 1);
    var end = new DateTime(2011, 7, 1);
    reader.Stub(x => x[0]).Return(start);
    reader.Stub(x => x[1]).Return(end);    ***<==== returns Jun 1 instead of Jul1

    var result = userType.NullSafeGet(reader, userType.PropertyNames, sessionImplementor, null);
    Assert.That(result, Is.EqualTo(new DateRange(start, end, DateRange.MaxSupportedPrecision)));

}
Expected: <6/1/2011 12:00 AM - 7/1/2011 12:00 AM>
But was:  <6/1/2011 12:00 AM - 6/1/2011 12:00 AM>

SUT (NHib CompositeUserType method)

public override object NullSafeGet(IDataReader dr, string[] names, ISessionImplementor session, object owner) {
    if (dr == null) return null;

    var foundStart = (DateTime)NHibernateUtil.DateTime.NullSafeGet(dr, names[0], session, owner);
    var foundEnd = (DateTime)NHibernateUtil.DateTime.NullSafeGet(dr, names[1], session, owner);

    var precision = DateRange.MaxSupportedPrecision;
    var startDp = _getDatePoint(foundStart, precision);
    var endDp = _getDatePoint(foundEnd, precision);

    return new DateRange(startDp, endDp, precision);
}

Solution

  • You are not mocking everything that is called by NHibernate. This is roughly what NHibernate does with a reader:

    ...
    int index = reader.GetOrdinal(name);
    ...
    if (reader.IsDBNull(index)) {
        return null;
    } else {
        ...
        val = rs[index];
        ...
    }
    

    Stub generated by Rhino will return 0 in response both GetOrdinal calls and it this is why it will return June1 both times. You can try to fix it by mocking GetOrdinal as well as indexer. Like this:

    var reader = MockRepository.GenerateStub<IDataReader>();
    var start = new DateTime(2011, 6, 1);
    var end = new DateTime(2011, 7, 1);
    
    reader.Stub(x => x.GetOrdinal(userType.PropertyNames[0])).Return(0);
    reader.Stub(x => x.GetOrdinal(userType.PropertyNames[1])).Return(1);
    
    reader.Stub(x => x[0]).Return(start);
    reader.Stub(x => x[1]).Return(end);
    

    But it might be worth reconsidering whether you really need to unit test UserType. It does not have a lot of responsibility other than calling NHibernate. Unit testing this class requires you to mock type you don't own (MS IDataReader). What's even worse is that this mock is used by another thirdparty (NHibernate). Essentially you need to look at NHibernate source code (which is what I did) to create a correct stub. Take a look at this article. It goes into a lot more details about why you should avoid mocking types that you don't own. You may be better off writing integration test for this class, using in-memory sqlite database.