Rhino.Mocks and Read Only Property Injection Part 2

Back to Listing

Rhino.Mocks and Read Only Property Injection Part 2


26 Jun, 2008


In a recent post, I discovered a flaw in my world domination plot. I had written a Unit Test to test my expectation that the method GetByID() of the class UserRepository returned the expected User. When UserRepository.GetById() is called, it calls IDataProvider.GetById(), which returns an instance User to the UserRepository which in turn returns it to the caller.

My UserRepository class delegates database access to the interface IDataProvider. This allows me to substitute any implementation of IDataProvider to satisfy this responsibility. In non testing code I use Dependancy Injection to map UserDataProvier to IDataProvider. But in testing code I do not really want to hit a database as it slows the testing process down. So I use Rhino.Mocks instead.

Rhino.Mocks is a framework for creating mock objects for use in Unit Testing. Provide Rhino.Mocks with an Interface and it will give you back an object that implements that interface. You can also tell it to expect certain calls against the interface and how it should respond.

My Unit Test looked something like this:

[Test]
public void GetByIdTest()  
{
    MockRepository mock = new MockRepository();

    IDataProvider dataProvider = (IDataProvider)mock.CreateMock<IDataProvider>();
    UserRepository target = new UserRepository(dataProvider);

    Expect.Call(dataProvider.GetById(1)).Return(new User() { Id = 1 });

    mock.ReplayAll();
    User u = target.GetById(1);
    mock.VerifyAll();

    Assert.AreEqual(1, u.Id);
}

As you can see this test, creates a MockRepository that is provided by the Rhino.Mocks framework. It then declares an instance of IDataProvider but delegates the creation of that instance to the MockRepository's CreateMock method. This tells Rhino.Mocks to create a dummy instance of the IDataProvider interface. Next we new up a UserRepository (the actual target of the unit test) and provide our mocked IDataProvider to it's constructor. Then we tell Rhino.Mocks to expect a call to our mock IDataProvider's GetById method and to return a new instance of User with it's Id property set to 1. Finally, we do our testing and validation.

This test fails to compile. The entity class User has read only properties. Id happens to be one of them. In the domain model for this particular application Id is a unique identifier and once a User instance is returned from the data access layer, it should never be modified.

So this presents a unique challenge, how do I test that UserRepository.GetById(1) returns an instance of User with an Id of 1?

I went down the path of trying to use Ninject (an Inversion of Control container), to inject the value in a newed up instance. But this had code smell for me. Why am I creating a dependency on Ninject to get my Unit Tests to work. That just seemed wrong to me. So I began digging in the Rhino.Mocks documentation wiki to see if it had a method for resolving this.

That was when Aiden Montgomery in the #ALT.NET IRC channel suggested that I use Reflection. He even went so far as to download my source from CodePlex and demonstrate what he was suggesting in my application.

The final test ended up looking something like this:

[Test]
public void GetByIdTest()  
{
    MockRepository mock = new MockRepository();

    Type userType = typeof(User);
    PropertyInfo pi = userType.GetProperty("Id");
    User user = new User();
    pi.SetValue(user, 1, null);

    IDataProvider dataProvider =
        (IDataProvider) mock.CreateMock<IDataProvider>();
    UserRepository target = new UserRepository(dataProvider);

    Expect.Call(dataProvider.GetById(1)).Return(user);

    mock.ReplayAll();
    User u = target.GetById(1);
    mock.VerifyAll();

    Assert.AreEqual(1, u.Id);
}

This of course passed, didn't add a non-BCL dependency and maintained the original intent of the test and the domain model. With all the shiny new toys, I was forgetting to see the forest through the trees and return to the simplest solution. Thanks to Aiden for bringing me back down from the clouds.

Share this story

Bobby Johnson

About Author

I am a passionate engineer with an interest in shipping quality software, building strong collaborative teams and continuous improvement of my skills, team and the product.

comments powered by Disqus
Back to top