Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Generic Methods Implementation in Microsoft Fakes

0.00/5 (No votes)
19 Oct 2014 1  
Describes how to implement generic methods for Microsoft Fakes Stubs and Shims using reflection constructed delegates at runtime

Introduction

Microsoft Fakes is a useful technology that allows to create fake stub and shim wrappers to use in testing process. However, it has some difficulties in implementing generic methods.

Problem

Assume that you have the following interface and class:

namespace GenericClassLibrary
{
    public interface InterfaceWithGenericMethods
    {
        void NonGenericMethod();

        T GenericMethod<T>();
    }

    public class GenericClass : InterfaceWithGenericMethods
    {
        public void NonGenericMethod()
        {
        }

        public T GenericMethod<T>()
        {
            return default(T);
        }
    }
}

Another project that uses fakes assembly of this class and has a stub of interface:

namespace GenericClassLibrary.Fakes
{
    [StubClass(typeof(InterfaceWithGenericMethods))]
    public class StubInterfaceWithGenericMethods : 
    StubBase<InterfaceWithGenericMethods>, InterfaceWithGenericMethods
    {
        public StubInterfaceWithGenericMethods();
        public FakesDelegates.Action NonGenericMethod;
        public void GenericMethodOf1<T>(FakesDelegates.Func<T> stub);
    }
}

Assigning a new method for a non-generic method is as easy as assigning a new delegate to NonGenericMethod property of stub:

var stub = new StubInterfaceWithGenericMethods();

stub.NonGenericMethod = Test;
stub.NonGenericMethod = () => Test();

Unfortunately, with generic methods, it is not that easy. If you have a new fake generic methods in other classes, you can't assign it directly!

public T NewFakeGenericMethod<T>()
{
    return default(T);
}

stub.GenericMethodOf1(NewFakeGenericMethod); // causes compilation error!

So you need to add stub method not for generic definition but for every implementation of generic method.

stub.GenericMethodOf1(NewFakeGenericMethod<ClassForGenericMethod>);

But what if the amount of classes you need to add is sufficient?
In my case, I needed to add generics for the whole assembly with 1000+ object types.
Inserting 1000+ lines of code didn't look good for me.

Solution

The idea is to construct the generic method and delegate at runtime and then invoke the stub method.
I have moved all code to a separate helper class, so it can easily be reused.
NOTE: This example works only for methods with single generic parameter.
However, if you want, you can adjust it for more parameters.

The complete code is listed here:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;

namespace FakesLibrary
{
    public class FakesHelper
    {
        public static void AddGenericStubMethod(object target,
            string addGenericStubMethodName, string genericMethodName, Type replacementType)
        {
            MethodInfo addGenericStubMethod = target.GetType().GetMethod(addGenericStubMethodName);
            MethodInfo genericMethod = target.GetType().GetMethod(genericMethodName);

            AddGenericStubMethod(target, addGenericStubMethod, genericMethod, replacementType);
        }

        public static void AddGenericStubMethod(object stub, 
            MethodInfo addGenericStubMethod, MethodInfo genericMethod, Type objectType)
        {
            try
            {
                Type genericDelegateParameterType = addGenericStubMethod.GetParameters()[0].ParameterType;

                Type[] genericTypeArgs = genericDelegateParameterType.GetGenericArguments();

                for (int i = 0; i < genericTypeArgs.Length; i++)
                    if (genericTypeArgs[i].IsGenericParameter)
                        genericTypeArgs[i] = objectType;
                    else if (genericTypeArgs[i].IsGenericType) // for types like List<T>
                        genericTypeArgs[i] = genericTypeArgs[i].GetGenericTypeDefinition().MakeGenericType(objectType);

                Type typeDelegateGenericDefinition = genericDelegateParameterType.GetGenericTypeDefinition();
                Type typeOfDelegate = typeDelegateGenericDefinition.MakeGenericType(genericTypeArgs);

                MethodInfo genericMethodOfType = genericMethod.MakeGenericMethod(objectType);

                Delegate delegateOfType = Delegate.CreateDelegate(typeOfDelegate, stub, genericMethodOfType);

                MethodInfo addStubMethodOfType = addGenericStubMethod.MakeGenericMethod(objectType);
                addStubMethodOfType.Invoke(stub, new object[] { delegateOfType });
            }
            catch
            {
                // oops... something went wrong, continue to do stuff for other assembly types in loop
            }
        }

        public static void AddGenericStubMethodsForAssemblyTypes(object stub, Assembly assembly,
            string addGenericStubMethodName, string genericMethodName, Type parentType = null)
        {
            MethodInfo addGenericStubMethod = stub.GetType().GetMethod(addGenericStubMethodName);
            MethodInfo genericMethod = stub.GetType().GetMethod(genericMethodName);

            AddGenericStubMethodsForAssemblyTypes(stub, assembly, addGenericStubMethod, genericMethod, parentType);
        }

        public static void AddGenericStubMethodsForAssemblyTypes(object stub, Assembly assembly,
            MethodInfo addGenericStubMethod, MethodInfo genericMethod, Type parentType = null)
        {
            foreach (Type type in assembly.GetTypes())
            {
                if (parentType == null || parentType.IsAssignableFrom(type))
                {
                    AddGenericStubMethod(stub, addGenericStubMethod, genericMethod, type);
                }
            }
        }
    }
}

Example code for the case described in the problem:

public class MyStubInterfaceWithGenericMethods : StubInterfaceWithGenericMethods
{
    public T NewFakeGenericMethod<T>()
    {
        return Activator.CreateInstance<T>();
    }

    public MyStubInterfaceWithGenericMethods()
        : base()
    {
        var assembly = typeof(ClassForGenericMethod).Assembly;
        FakesHelper.AddGenericStubMethodsForAssemblyTypes
        (this, assembly, "GenericMethodOf1", "NewFakeGenericMethod");
    }
}

public class FakesTest
{
    [STAThread]
    public static void Main(string[] args)
    {
        var stub = new MyStubInterfaceWithGenericMethods() as InterfaceWithGenericMethods;
        var test = stub.GenericMethod<ClassForGenericMethod>();
    }
}

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here