Question

We have been using Simple.Mocking as our mocking framework for unit testing. Recently, I made a change to our ICacheService, which is heavily used in our unit testing and mocking.

The change I made was add a method similar to the following:

bool TrySetCache(string key,T data,ref T value);

The semantics of this implementation is irrelevant so please do not give feedback on the use of reference-type parameters. :)

Once our CI build kicked off and ran our automated tests, they all failed due to mock type creation failing.

I looked deeper into the project and realized that it was failing when it tried to mock the generic reference-type parameter.

The following coee handles emitting IL for the mocked method parameters:

var baseMethodParameters = baseMethod.GetParameters();

        ilGenerator.Emit(OpCodes.Ldc_I4, baseMethodParameters.Length);
        ilGenerator.Emit(OpCodes.Newarr, typeof(object));
        ilGenerator.Emit(OpCodes.Stloc_2);

        for (int i = 0; i < baseMethodParameters.Length; i++)
        {
            var parameter = baseMethodParameters[i];

            var parameterType = parameter.ParameterType;

            ilGenerator.Emit(OpCodes.Ldloc_2);
            ilGenerator.Emit(OpCodes.Ldc_I4, i);

            ilGenerator.Emit(OpCodes.Ldarg, i + 1);

            if (parameterType.IsByRef)
            {
                parameterType = parameterType.GetRealTypeForByRefType();
                ilGenerator.EmitLoadIndirect(parameterType);
            }

            EmitBoxValue(ilGenerator, parameterType);

            ilGenerator.Emit(OpCodes.Stelem_Ref);
        }

More specifically, the code for handling the reference-type parameter:

if (parameterType.IsByRef)
            {
                parameterType = parameterType.GetRealTypeForByRefType();
                ilGenerator.EmitLoadIndirect(parameterType);
            }

This calls the extension method that is failing when a generic reference-type parameter is used:

const string ByRefSpecifier = "&";

    public static Type GetRealTypeForByRefType(this Type type)
    {
        if (!type.IsByRef)
            throw new InvalidOperationException();

        return Type.GetType(type.AssemblyQualifiedName.Replace(ByRefSpecifier, string.Empty), true);
    }

The return statement fails.

I would like to fix this, but I am not sure if its actually possible to infer the usage and mock a generic reference-type parameter.

If it is in fact not possible, then I will just end up adding a check to see if it is a generic-type parameter, and if it is, give an error message so users dont need to troubleshoot for too long to figure out the issue.

IMPORTANT NOTE One thing I did notice when I was debugging this was that when it reflects on my ICacheService.TrySetCache(string key,T data,ref T value), it does not mark the last parameter as a generic parameter type.

Was it helpful?

Solution

Since your type is a generic parameter, AssemblyQualifiedName will be null. Instead, parameterType.GetRealTypeForByRefType() should almost certainly just be replaced by parameterType.GetElementType().

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top