RhinoMocks: the Int32 vs Int64 conundrum

In these last couple of days I’ve been busy trying to refactor some apps which are starting to show their age. It hasn’t been fun because unfortunately I didn’t have a high enough code coverage to perform the refactoring safely (oh well, life sucks sometime!). Even though it’s not the most pleasant thing to do in the world (hell, I can remember quite a lot of way more interesting things to do Smile), doing this kind of work has been…how shall I put it…interesting.

Today I got stuck for at least an hour trying to understand why a RhinoMock stub wasn’t being called as it should. In order to illustrate the problem, let me present you with the code being tested (this is only for illustration purposes):

public class StudentService {
    private readonly ISession _session;
    public StudentService(ISession session) {
        _session = session;
    }
    public Student GetStudent(Int64 studentId) {
        using(var tran = _session.BeginTransaction()) {
                return _session.Get<Student>(studentId);
        }
    }
}

In the real world, this service would implement an existing IStudentService contract and I’d be using code contracts to ensure that _session would always be a valid reference. In my real world example, I had lots of other things going on too, but the previous snippet is more than enough for me to make my point. Now, being a conscious developer, I knew I had to update the existing tests. I was already using RhinoMocks, so I’ve added structuremap’s RhinoAutoMocker to simplify the testing code.

After refactoring the code, I’ve ended up with something that resembled the following snippet:

[TestFixture]
public class StudentServiceReturnsExistingStudent {
    private readonly RhinoAutoMocker<StudentService> _autoMocker;
    private readonly  ISession _session;
    private readonly ITransaction _tran;
    private readonly Student _std;
    private const Int32 _id = 1;
    public StudentServiceReturnsExistingStudent() {
        _autoMocker = new RhinoAutoMocker<StudentService>();
        _session = _autoMocker.Get<ISession>();
        _tran = _autoMocker.Get<ITransaction>();
        _std = new Student {Id = 1};
    }

    [Test]
    public void Test() {
        Arrange();
        var std = _autoMocker.ClassUnderTest.GetStudent(_id);
        Assert.AreEqual(_std, std);
        _session.VerifyAllExpectations();
    }

    private void Arrange() {
        _session.Expect(s => s.BeginTransaction())
            .Return(_tran);
        _session.Expect(s => s.Get<Student>(_id))
            .Return(_std);
    }
}

Ah, now let’s run the test. What? not working?

image

Not possible…what the hell is going on here? After looking several times and checking the types were correct (in my case, I had Dto.Student and Student, so I double checked if the types being expected were of the correct type), I’ve decided to ignore the arguments being passed into Get:

_session.Expect(s => s.Get<Student>(_id))
            .IgnoreArguments()
            .Return(_std);

This small change was enough for making the test pass. In practice, this meant that there was something wrong with the setup of arguments in the Expect call. And sure, that was the problem! If you look carefully at GetStudent, you’ll notice that it expects an Int64. However, my test code was configured so that Get would receive an integer (Int32). Interestingly, calling GetStudent with an integer works without a problem (since going from int to long is considered to be a safe conversion). However, that does not happen when you’re building “RhinoMock expectations”. Moral of the story: if you think you’ve set up the expectations correctly and your test is not producing the expected results, double check the types of the arguments you’re setting up in the expectations.

And that’s it for now. Stay tuned for more.

~ by Luis Abreu on September 27, 2012.

Leave a comment