Simplifying Rhino.Mocks

[UPDATE]: Example code is updated. I realized that Kind.Multi is not needed, since you can infer that from passed parameters (when ctorArgs are present, user obviously wants to create MultiMock). I also changed default Kind to Relaxed, since this is the most common one.

Ayende wrote today about his ideas for new version of Rhino.Mocks. I like the new syntax (looks similar to what MoQ offers), but there’s one more change I’d like to see.

Here’s the list of all methods of MockRepository, used to create some kind of mock:

 

public T CreateMock<T>(params object[] argumentsForConstructor);
public object CreateMock(Type type, params object[] argumentsForConstructor);
public T CreateMockWithRemoting<T>(params object[] argumentsForConstructor);
public object CreateMockWithRemoting(Type type, params object[] argumentsForConstructor);
public T CreateMultiMock<T>(params Type[] extraTypes);
public object CreateMultiMock(Type mainType, params Type[] extraTypes);
public T CreateMultiMock<T>(Type[] extraTypes, params object[] argumentsForConstructor);
public object CreateMultiMock(Type mainType, Type[] extraTypes, params object[] argumentsForConstructor);
public T DynamicMock<T>(params object[] argumentsForConstructor);
public object DynamicMock(Type type, params object[] argumentsForConstructor);
public T DynamicMockWithRemoting<T>(params object[] argumentsForConstructor);
public object DynamicMockWithRemoting(Type type, params object[] argumentsForConstructor);
public T DynamicMultiMock<T>(params Type[] extraTypes);
public object DynamicMultiMock(Type mainType, params Type[] extraTypes);
public T DynamicMultiMock<T>(Type[] extraTypes, params object[] argumentsForConstructor);
public object DynamicMultiMock(Type mainType, Type[] extraTypes, params object[] argumentsForConstructor);
public static T GenerateStub<T>(params object[] argumentsForConstructor);
public static object GenerateStub(Type type, params object[] argumentsForConstructor);
public T PartialMock<T>(params object[] argumentsForConstructor) where T : class;
public object PartialMock(Type type, params object[] argumentsForConstructor);
public T PartialMultiMock<T>(params Type[] extraTypes);
public T PartialMultiMock<T>(Type[] extraTypes, params object[] argumentsForConstructor);
public object PartialMultiMock(Type type, params Type[] extraTypes);
public object PartialMultiMock(Type type, Type[] extraTypes, params object[] argumentsForConstructor);
public T Stub<T>(params object[] argumentsForConstructor);
public object Stub(Type type, params object[] argumentsForConstructor);

26 methods including 2 static methods (most of them in 2 flavors: generic and 1.1-like). I guess that’s a lot. I can imagine this can be a little overwhelming for new users.

I understand that tearing interface of MockRepository would not be the best idea, but I think that creating a Facade to it might simplify things a lot.

public class MockRepositorySlim
{
    private static readonly Dictionary<Kind, Func<MockRepository, Type, object[], Type[], object>> _mockMethods =
        new Dictionary<Kind, Func<MockRepository, Type, object[], Type[], object>>(9)
            {
                {Kind.Relaxed,(r, t, cp, et) => et.Length > 0 ? r.DynamicMultiMock(t, et, cp) : r.DynamicMock(t, cp)},
                {Kind.Strict, (r, t, cp, et) => et.Length > 0 ? r.CreateMultiMock(t, et, cp) : r.CreateMock(t, cp)},
                {Kind.Stub, (r, t, cp, et) => r.Stub(t, cp)},
                {Kind.Partial,(r, t, cp, et) => et.Length > 0 ? r.PartialMultiMock(t, et, cp) : r.PartialMock(t, cp)},
                {Kind.Strict | Kind.WithRemoting, (r, t, cp, et) => r.CreateMockWithRemoting(t, cp)},
                {Kind.Relaxed | Kind.WithRemoting, (r, t, cp, et) => r.DynamicMockWithRemoting(t, cp)}
            };
 
    private readonly MockRepository _repo = new MockRepository();
 
    public TTypeToMock Mock<TTypeToMock>()
    {
        return Mock<TTypeToMock>(Kind.Relaxed, new Type[0], new object[0]);
    }
 
    public TTypeToMock Mock<TTypeToMock>(Kind mockKind)
    {
        return Mock<TTypeToMock>(mockKind, new Type[0], new object[0]);
    }
 
    public TTypeToMock Mock<TTypeToMock>(Kind mockKind, params object[] ctorArgs)
    {
        return Mock<TTypeToMock>(mockKind, new Type[0], ctorArgs);
    }
 
    public TTypeToMock Mock<TTypeToMock>(Kind mockKind, params Type[] extraTypes)
    {
        return Mock<TTypeToMock>(mockKind, extraTypes, new object[0]);
    }
 
    public TTypeToMock Mock<TTypeToMock>(Kind mockKind, Type[] extraTypes, params object[] ctorArgs)
    {
        if (extraTypes == null) throw new ArgumentNullException("extraTypes");
        if (ctorArgs == null) throw new ArgumentNullException("ctorArgs");
 
        if (mockKind == Kind.WithRemoting)
            mockKind &= Kind.Strict;
        else if (!_mockMethods.ContainsKey(mockKind))
        {
            if ((mockKind & Kind.WithRemoting) != 0)
                throw new ArgumentException("This kind of mock does not support remoting.");
            throw new ArgumentException("Invalid mock kind.", "mockKind");
        }
        //NOTE: possibly with lock in this call
        var mock = (TTypeToMock) _mockMethods[mockKind](_repo, typeof (TTypeToMock), ctorArgs, extraTypes);
        return mock;
    }
}
internal static class KindExtensions
{
    public static bool IsSet(this Kind kind, Kind value)
    {
        return (kind & value) == 0;
    }
}
 
 
[Flags]
public enum Kind
{
    Strict=0,
    WithRemoting=1,
    Relaxed=2,
    Stub=4,
    Partial=8
}

Now we have only 5 methods, 4 of which call the 5th one. I guess that’s simpler solution. Is it any better?

Technorati Tags: , ,

Comments

Xavier says:

Completely agree: i’ve just discovered Rhino Mock and it is really, really hard to understand what syntax to use!