0

I am trying to create a run-time mock, which will have a specific behaviour for every method, which will be called. For example this mock should always return null - which is already working - or raise an exception for a not reachable database or in this specific case throw an Argument Exception. I know, that this could be done fairly easy with a mocking framework. But since I have to use this via Spring.NET, I can not use a mocking framework, as far as I know. This is because I have to give a type into the StaticApplicationContext. If there is an easier way to create mock types, let me know.

The code below will throw an Expected: But was: (Common Language Runtime detected an invalid program.) at TypeMock.Foo() at TypeBuilderTest.

    public interface IInterfaceWithObject
    {
        String Foo();
    }

    [Test]
    public void MinimalExample()
    {
        //build expression
        var constructorInfo = typeof(ArgumentNullException).GetConstructor(new Type[0]);
        Assert.IsNotNull(constructorInfo);
        Expression expression = Expression.Throw(Expression.New(constructorInfo));

        //create type
        // based upon https://stackoverflow.com/questions/38345486/dynamically-create-a-class-by-interface
        var ab = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("TestAssembly"), AssemblyBuilderAccess.Run);
        var mb = ab.DefineDynamicModule("Test");

        TypeBuilder typeBuilder = mb.DefineType("TypeMock");

        typeBuilder.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);

        typeBuilder.AddInterfaceImplementation(typeof(IInterfaceWithObject));
        List<MethodInfo> methods = new List<MethodInfo>(typeof(IInterfaceWithObject).GetMethods());
        foreach (Type interf in typeof(IInterfaceWithObject).GetInterfaces())
        {
            foreach (MethodInfo method in interf.GetMethods())
                if (!methods.Contains(method))
                    methods.Add(method);
        }
        foreach (var imethod in methods)
        {
            var parameters = imethod.GetParameters();
            List<Type> parameterTypes = new List<Type>();
            foreach (var parameter in parameters)
            {
                parameterTypes.Add(parameter.ParameterType);
            }
            var method =
              typeBuilder.DefineMethod
              (
                "@@" + imethod.Name,
                MethodAttributes.Public | MethodAttributes.Static,
                imethod.ReturnType,
                parameterTypes.ToArray()
              );
            var thisParameter = Expression.Parameter(typeof(IInterfaceWithObject), "this");
            var bodyExpression = Expression.Lambda
                (expression
                    ,
                    thisParameter
                );

            bodyExpression.CompileToMethod(method);
            var stub =
              typeBuilder.DefineMethod(imethod.Name, MethodAttributes.Public | MethodAttributes.Virtual, imethod.ReturnType, parameterTypes.ToArray());
            var il = stub.GetILGenerator();
            il.Emit(OpCodes.Ldarg_0);
            il.EmitCall(OpCodes.Call, method, null);
            il.Emit(OpCodes.Ret);

            typeBuilder.DefineMethodOverride(stub, imethod);
        }
        Type type =  typeBuilder.CreateType();

        //create instance via spring.net
        StaticApplicationContext context = new StaticApplicationContext();
        MutablePropertyValues value = new MutablePropertyValues();
        string objectName = "Integer";
        context.RegisterSingleton(objectName, type, value);
        var objectInstance = (IInterfaceWithObject)context.GetObject(objectName);
        Assert.Throws<ArgumentNullException>(() => objectInstance.Foo());
    }
chbest
  • 23
  • 4
  • Can you explain why you would want to test against a mock? Wouldn't it be easier to just create a fake (class) that implements `IInterfaceWithObject`? – Lennart Stoop Aug 23 '18 at 14:37
  • It would be easier and it is what I did so far. But currently I have to create it for at least five different interfaces and for different kind of exceptions and / or return values, like null. That is possible, but that is also a lot of duplicate code, which is not really necessary. – chbest Aug 24 '18 at 10:25

0 Answers0