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());
}