2
votes

I am working on writing some unit tests with Rhino Mocks for a WPF application written in C# that uses Unity for dependency injection and uses MVVM achitechture. I'm not very experienced with unit testing with Rhino Mocks so I'm not sure what the best practices are yet.

In the view model I'm going to be writing unit tests for, there is a dependency injected data access class, we'll call it DataAccess, that is from an external assembly that I don't control. Only a single instance is registered with the Unity container because DataAccess has a cache, and it's desirable to share that instance across the whole application via the Unity container in order to improve performance. Now I need to mock DataAccess in my unit test obviously because I don't have control over the data in the database. I want to stub or expect the Retrieve method to return a specific value, but DataAccess doesn't implement an interface and the method I need to stub is not virtual. From what I've read online, Rhino Mocks can't override a non-virtual method, and the only other option is to mock an interface with the methods you want to override. Neither of those options are valid for this case because I don't own the DataAccess code. I've heard that TypeMock has the ability to override non-virtual methods but convincing my company to switch to a paid mocking library is probably not going to happen so I'm stuck with Rhino Mocks. So is there a way to mock this class and override this method with Rhino Mocks?

public class DataAccess //in an external assembly
{
    public TEntity Retrieve(TKey key);
}

public class ViewModel //in the client project
{
    [Dependency]
    public DataAccess DataAccess { get; set; }
}

I came up with a possible solution but it doesn't use mocks and I'd like to use mocks because there are many places where we have this situation. My idea is to create a wrapper class (fake) for DataAccess that has the same methods and properties as DataAccess except all of the important methods/properties are marked virtual, and I'll put that class in my test project. Then in the unit test initializer I will register a type mapping from DataAccess to the wrapper class in my Unity container so that the VM instantiated in my unit test will get an instance of the wrapper class instead. Do you see any drawbacks to going in this direction aside from creating a bunch of wrapper classes?

public class DataAccessFake : DataAccess //in the test project
{
    public new virtual TEntity Retrieve(TKey key) //hides the DataAccess Retrieve with a virtual one
    {
        return base.Retrieve(key);
    }
}
1
Does this actually work? The objects that depend on DataAccess would still call DataAccess.Retrieve and not DataAccessFake.Retrieve, which are two totally different methods.Yacoub Massad
The new keyword indicates that the method hides its inherited member. So yes, it should call the fake method.StarChar
Yes. A simple test shows this works.StarChar

1 Answers

0
votes

Here is some code that I have tested to show that the suggested approach will not work:

class Program
{
    static void Main(string[] args)
    {
        ViewModel vm = new ViewModel()
        {
            DataAccess = new DataAccessFake()
        };

        string result = vm.Test("test"); //this returns "test_original"
    }
}

public class ViewModel
{
    public DataAccess DataAccess { get; set; }

    public string Test(string key)
    {
        return DataAccess.Retrieve(key);
    }
}

public class DataAccess
{
    public string Retrieve(string key)
    {
        return key + "_original";
    }
}

public class DataAccessFake : DataAccess
{
    public new virtual string Retrieve(string key)
    {
        return key + "_fake";
    }
}

If you run this code, the output of the Test method will be "test_original" which shows that the DataAccessFake.Retrieve method was never called. You can also place a breakpoint in such method to verify.

Since ViewModel depends on DataAccess, it will always call DataAccess.Retrieve which is a totally different method than DataAccessFake.Retrieve.

Of course, the behavior would be very different if DataAccessFake.Retrieve had been an overridden method of DataAccess.Retrieve.